25 Commits

Author SHA1 Message Date
AnErrupTion
4b9ea3d7cb Backport: Possibly fix .Xresources not being loaded
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 21:18:33 +01:00
AnErrupTion
83984dc493 Backport: Don't set XDG_CURRENT_DESKTOP and XDG_SESSION_DESKTOP if they're empty
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 22:54:34 +01:00
AnErrupTion
a9449742d6 Backport: Try to create /etc/pam.d and /usr/bin everytime when installing
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 22:52:34 +01:00
AnErrupTion
ea2dec50f5 Update README.md
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-02 19:40:25 +02:00
AnErrupTion
fadbbf676a Support Zig 0.13.0
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-02 19:40:09 +02:00
AnErrupTion
042aa50ff0 Start Ly v1.0.3 development cycle
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-02 19:37:11 +02:00
AnErrupTion
56202bc30e Backport: Swap /usr/bin and /usr/sbin in PATH
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-01 18:02:03 +02:00
AnErrupTion
7300247e57 Backport: Update zigini (fixes an escaping bug)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-01 13:29:34 +02:00
AnErrupTion
3ca2e8524b Fix mcookie usage (fixes #669)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-01 13:20:23 +02:00
AnErrupTion
40d180da63 Backport: Only shutdown or restart after deinitializing everything
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-30 12:04:25 +02:00
AnErrupTion
75cc971f9c Backport: Update zigini (fixes incorrect comment parsing)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-30 09:51:42 +02:00
AnErrupTion
9374d2df32 Backport: Fix clock & bigclock not updating without input
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-29 14:19:47 +02:00
AnErrupTion
67fd024f6a Revert "Redirect stderr to systemd journal in service (#621)"
This reverts commit 3d8d8d67df.

Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-29 13:35:04 +02:00
AnErrupTion
5796720a9c Backport: Add missing supervise symlink on runit
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-28 11:35:23 +02:00
AnErrupTion
802ad6bbed Backport: Set PAM_TTY
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 22:45:20 +02:00
AnErrupTion
7ece95965b Backport: Fix ~/.profile not being loaded with Fish
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 22:44:35 +02:00
AnErrupTion
10cd9615ef Backport: Fix possible overflow with TTY ID
Co-authored-by: Kevin Morris <kevr@0cost.org>
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 21:07:41 +02:00
AnErrupTion
a807e8e11c Backport: Use default PRNG and retrieve better seed
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 14:25:30 +02:00
AnErrupTion
d87344330a Start Ly v1.0.2 development cycle
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 14:18:09 +02:00
AnErrupTion
a042749a72 Backport: Make runit run and finish scripts executable
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-26 21:59:21 +02:00
AnErrupTion
391104cf34 Backport: Fix documentation issue about DOOM animation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-26 19:34:46 +02:00
AnErrupTion
8e534c7bcd Backport: Fix incorrect shebang in xsetup.sh
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-26 19:34:12 +02:00
AnErrupTion
6b7e7be387 Backport: Use octal prefix for file modes in build.zig
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-26 19:32:30 +02:00
AnErrupTion
53d252232f Backport: Fix dest_directory embedded in binary
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-03 09:52:24 +02:00
AnErrupTion
5cdd6af738 Start Ly v1.0.1 development cycle
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-03 09:52:00 +02:00
67 changed files with 1918 additions and 4357 deletions

View File

@@ -1,68 +0,0 @@
name: Bug report
description: File a bug report.
title: "[Bug] "
labels: ["bug"]
body:
- type: checkboxes
id: prerequisites
attributes:
label: Pre-requisites
description: By submitting this issue, you agree to have done the following.
options:
- label: I have looked for any other duplicate issues
required: true
- type: input
id: version
attributes:
label: Ly version
description: The output of `ly --version`. Please note that only Ly v1.1.0 and above are supported.
placeholder: 1.1.0-dev.12+2b0301c
validations:
required: true
- type: textarea
id: observed
attributes:
label: Observed behavior
description: What happened?
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: What did you expect to happen instead?
validations:
required: true
- type: input
id: desktop
attributes:
label: OS + Desktop environment/Window manager
description: Which OS and DE (or WM) did you use when observing the problem?
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps to reproduce
description: What **exactly** can someone else do in order to observe the problem you observed?
placeholder: |
1. Authenticate with ...
2. Go to ...
3. Create file ...
4. Log out and log back in
5. Observe error
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant logs
description: |
Please copy and paste 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.
If you're using the latest code on master (for v1.1.0), including your session log (found at /var/log/ly-session.log unless modified) is a good idea. (But make sure it's relevant!)
render: shell
- type: textarea
id: moreinfo
attributes:
label: Additional information
description: If you have any additional information that might be helpful in reproducing the problem, please provide it here.

View File

@@ -1,22 +0,0 @@
name: Feature request
description: Request a new feature or enhancement.
title: "[Feature] "
labels: ["feature"]
body:
- type: checkboxes
id: prerequisites
attributes:
label: Pre-requisites
description: By submitting this issue, you agree to have done the following.
options:
- label: I have looked for any other duplicate issues
required: true
- label: I have confirmed the requested feature doesn't exist in the latest version in development
required: true
- type: textarea
id: wanted
attributes:
label: Wanted behavior
description: What do you want to be added? Describe the behavior clearly.
validations:
required: true

BIN
.github/screenshot.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

408
build.zig
View File

@@ -1,16 +1,7 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const PatchMap = std.StringHashMap([]const u8); const min_zig_string = "0.12.0";
const InitSystem = enum {
systemd,
openrc,
runit,
s6,
dinit,
};
const min_zig_string = "0.14.0";
const current_zig = builtin.zig_version; const current_zig = builtin.zig_version;
// Implementing zig version detection through compile time // Implementing zig version detection through compile time
@@ -21,34 +12,27 @@ comptime {
} }
} }
const ly_version = std.SemanticVersion{ .major = 1, .minor = 1, .patch = 2 }; const ly_version = std.SemanticVersion{ .major = 1, .minor = 0, .patch = 3 };
var dest_directory: []const u8 = undefined; var dest_directory: []const u8 = undefined;
var config_directory: []const u8 = undefined; var data_directory: []const u8 = undefined;
var prefix_directory: []const u8 = undefined; var exe_name: []const u8 = undefined;
var executable_name: []const u8 = undefined;
var init_system: InitSystem = undefined; const ProgressNode = if (current_zig.minor == 12) *std.Progress.Node else std.Progress.Node;
var default_tty_str: []const u8 = undefined;
pub fn build(b: *std.Build) !void { pub fn build(b: *std.Build) !void {
dest_directory = b.option([]const u8, "dest_directory", "Specify a destination directory for installation") orelse ""; dest_directory = b.option([]const u8, "dest_directory", "Specify a destination directory for installation") orelse "";
config_directory = b.option([]const u8, "config_directory", "Specify a default config directory (default is /etc). This path gets embedded into the binary") orelse "/etc"; data_directory = b.option([]const u8, "data_directory", "Specify a default data directory (default is /etc/ly). This path gets embedded into the binary") orelse "/etc/ly";
prefix_directory = b.option([]const u8, "prefix_directory", "Specify a default prefix directory (default is /usr)") orelse "/usr"; exe_name = b.option([]const u8, "name", "Specify installed executable file name (default is ly)") orelse "ly";
executable_name = b.option([]const u8, "name", "Specify installed executable file name (default is ly)") orelse "ly";
init_system = b.option(InitSystem, "init_system", "Specify the target init system (default is systemd)") orelse .systemd; const bin_directory = try b.allocator.dupe(u8, data_directory);
data_directory = try std.fs.path.join(b.allocator, &[_][]const u8{ dest_directory, data_directory });
const build_options = b.addOptions(); const build_options = b.addOptions();
build_options.addOption([]const u8, "data_directory", bin_directory);
const version_str = try getVersionStr(b, "ly", ly_version); const version_str = try getVersionStr(b, "ly", ly_version);
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;
default_tty_str = try std.fmt.allocPrint(b.allocator, "{d}", .{default_tty});
build_options.addOption([]const u8, "config_directory", config_directory);
build_options.addOption([]const u8, "prefix_directory", prefix_directory);
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(bool, "enable_x11_support", enable_x11_support);
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
@@ -70,19 +54,23 @@ pub fn build(b: *std.Build) !void {
exe.addIncludePath(b.path("include")); exe.addIncludePath(b.path("include"));
exe.linkSystemLibrary("pam"); exe.linkSystemLibrary("pam");
if (enable_x11_support) exe.linkSystemLibrary("xcb"); exe.linkSystemLibrary("xcb");
exe.linkLibC(); exe.linkLibC();
// HACK: Only fails with ReleaseSafe, so we'll override it.
const translate_c = b.addTranslateC(.{ const translate_c = b.addTranslateC(.{
.root_source_file = b.path("include/termbox2.h"), .root_source_file = b.path("include/termbox2.h"),
.target = target, .target = target,
.optimize = optimize, .optimize = if (optimize == .ReleaseSafe) .ReleaseFast else optimize,
}); });
translate_c.defineCMacroRaw("TB_IMPL"); translate_c.defineCMacroRaw("TB_IMPL");
translate_c.defineCMacro("TB_OPT_ATTR_W", "32"); // Enable 24-bit color support + styling (32-bit)
const termbox2 = translate_c.addModule("termbox2"); const termbox2 = translate_c.addModule("termbox2");
exe.root_module.addImport("termbox2", termbox2); exe.root_module.addImport("termbox2", termbox2);
if (optimize == .ReleaseSafe) {
std.debug.print("warn: termbox2 module is being built in ReleaseFast due to a bug.\n", .{});
}
b.installArtifact(exe); b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe); const run_cmd = b.addRunArtifact(exe);
@@ -94,54 +82,98 @@ pub fn build(b: *std.Build) !void {
const run_step = b.step("run", "Run the app"); const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step); run_step.dependOn(&run_cmd.step);
const installexe_step = b.step("installexe", "Install Ly and the selected init system service"); const installexe_step = b.step("installexe", "Install Ly");
installexe_step.makeFn = Installer(true).make; installexe_step.makeFn = ExeInstaller(true).make;
installexe_step.dependOn(b.getInstallStep()); installexe_step.dependOn(b.getInstallStep());
const installnoconf_step = b.step("installnoconf", "Install Ly and the selected init system service, but not the configuration file"); const installnoconf_step = b.step("installnoconf", "Install Ly without its configuration file");
installnoconf_step.makeFn = Installer(false).make; installnoconf_step.makeFn = ExeInstaller(false).make;
installnoconf_step.dependOn(b.getInstallStep()); installnoconf_step.dependOn(b.getInstallStep());
const uninstallexe_step = b.step("uninstallexe", "Uninstall Ly and remove the selected init system service"); const installsystemd_step = b.step("installsystemd", "Install the Ly systemd service");
uninstallexe_step.makeFn = Uninstaller(true).make; installsystemd_step.makeFn = ServiceInstaller(.Systemd).make;
installsystemd_step.dependOn(installexe_step);
const uninstallnoconf_step = b.step("uninstallnoconf", "Uninstall Ly and remove the selected init system service, but keep the configuration directory"); const installopenrc_step = b.step("installopenrc", "Install the Ly openrc service");
uninstallnoconf_step.makeFn = Uninstaller(false).make; installopenrc_step.makeFn = ServiceInstaller(.Openrc).make;
installopenrc_step.dependOn(installexe_step);
const installrunit_step = b.step("installrunit", "Install the Ly runit service");
installrunit_step.makeFn = ServiceInstaller(.Runit).make;
installrunit_step.dependOn(installexe_step);
const uninstallall_step = b.step("uninstallall", "Uninstall Ly and all services");
uninstallall_step.makeFn = uninstallall;
} }
pub fn Installer(install_config: bool) type { pub fn ExeInstaller(install_conf: bool) type {
return struct { return struct {
pub fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void { pub fn make(step: *std.Build.Step, progress: ProgressNode) !void {
const allocator = step.owner.allocator; _ = progress;
try install_ly(step.owner.allocator, install_conf);
var patch_map = PatchMap.init(allocator);
defer patch_map.deinit();
try patch_map.put("$DEFAULT_TTY", default_tty_str);
try patch_map.put("$CONFIG_DIRECTORY", config_directory);
try patch_map.put("$PREFIX_DIRECTORY", prefix_directory);
try patch_map.put("$EXECUTABLE_NAME", executable_name);
try install_ly(allocator, patch_map, install_config);
try install_service(allocator, patch_map);
} }
}; };
} }
fn install_ly(allocator: std.mem.Allocator, patch_map: PatchMap, install_config: bool) !void { const InitSystem = enum {
const ly_config_directory = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly" }); Systemd,
Openrc,
Runit,
};
pub fn ServiceInstaller(comptime init_system: InitSystem) type {
return struct {
pub fn make(step: *std.Build.Step, progress: ProgressNode) !void {
_ = progress;
const allocator = step.owner.allocator;
switch (init_system) {
.Openrc => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/etc/init.d" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
std.fs.cwd().makePath(ly_config_directory) catch { try std.fs.cwd().copyFile("res/ly-openrc", service_dir, exe_name, .{ .override_mode = 0o755 });
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_config_directory}); },
.Runit => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/etc/sv/ly" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const supervise_path = try std.fs.path.join(allocator, &[_][]const u8{ service_path, "supervise" });
try std.fs.cwd().copyFile("res/ly-runit-service/conf", service_dir, "conf", .{});
try std.fs.cwd().copyFile("res/ly-runit-service/finish", service_dir, "finish", .{ .override_mode = 0o755 });
try std.fs.cwd().copyFile("res/ly-runit-service/run", service_dir, "run", .{ .override_mode = 0o755 });
try std.fs.cwd().symLink("/run/runit/supervise.ly", supervise_path, .{});
},
.Systemd => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/usr/lib/systemd/system" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
try std.fs.cwd().copyFile("res/ly.service", service_dir, "ly.service", .{ .override_mode = 0o644 });
},
}
}
};
}
fn install_ly(allocator: std.mem.Allocator, install_config: bool) !void {
std.fs.cwd().makePath(data_directory) catch {
std.debug.print("warn: {s} already exists as a directory.\n", .{data_directory});
}; };
const ly_lang_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/lang" }); const lang_path = try std.fs.path.join(allocator, &[_][]const u8{ data_directory, "/lang" });
std.fs.cwd().makePath(ly_lang_path) catch { std.fs.cwd().makePath(lang_path) catch {
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_lang_path}); std.debug.print("warn: {s} already exists as a directory.\n", .{data_directory});
}; };
var current_dir = std.fs.cwd();
{ {
const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin" }); const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/usr/bin" });
std.fs.cwd().makePath(exe_path) catch { std.fs.cwd().makePath(exe_path) catch {
if (!std.mem.eql(u8, dest_directory, "")) { if (!std.mem.eql(u8, dest_directory, "")) {
std.debug.print("warn: {s} already exists as a directory.\n", .{exe_path}); std.debug.print("warn: {s} already exists as a directory.\n", .{exe_path});
@@ -151,46 +183,45 @@ fn install_ly(allocator: std.mem.Allocator, patch_map: PatchMap, install_config:
var executable_dir = std.fs.cwd().openDir(exe_path, .{}) catch unreachable; var executable_dir = std.fs.cwd().openDir(exe_path, .{}) catch unreachable;
defer executable_dir.close(); defer executable_dir.close();
try installFile("zig-out/bin/ly", executable_dir, exe_path, executable_name, .{}); try current_dir.copyFile("zig-out/bin/ly", executable_dir, exe_name, .{});
} }
{ {
var config_dir = std.fs.cwd().openDir(ly_config_directory, .{}) catch unreachable; var config_dir = std.fs.cwd().openDir(data_directory, .{}) catch unreachable;
defer config_dir.close(); defer config_dir.close();
if (install_config) { if (install_config) {
const patched_config = try patchFile(allocator, "res/config.ini", patch_map); try current_dir.copyFile("res/config.ini", config_dir, "config.ini", .{});
try installText(patched_config, config_dir, ly_config_directory, "config.ini", .{});
} }
try current_dir.copyFile("res/xsetup.sh", config_dir, "xsetup.sh", .{});
const patched_setup = try patchFile(allocator, "res/setup.sh", patch_map); try current_dir.copyFile("res/wsetup.sh", config_dir, "wsetup.sh", .{});
try installText(patched_setup, config_dir, ly_config_directory, "setup.sh", .{ .mode = 0o755 });
} }
{ {
var lang_dir = std.fs.cwd().openDir(ly_lang_path, .{}) catch unreachable; var lang_dir = std.fs.cwd().openDir(lang_path, .{}) catch unreachable;
defer lang_dir.close(); defer lang_dir.close();
try installFile("res/lang/cat.ini", lang_dir, ly_lang_path, "cat.ini", .{}); try current_dir.copyFile("res/lang/cat.ini", lang_dir, "cat.ini", .{});
try installFile("res/lang/cs.ini", lang_dir, ly_lang_path, "cs.ini", .{}); try current_dir.copyFile("res/lang/cs.ini", lang_dir, "cs.ini", .{});
try installFile("res/lang/de.ini", lang_dir, ly_lang_path, "de.ini", .{}); try current_dir.copyFile("res/lang/de.ini", lang_dir, "de.ini", .{});
try installFile("res/lang/en.ini", lang_dir, ly_lang_path, "en.ini", .{}); try current_dir.copyFile("res/lang/en.ini", lang_dir, "en.ini", .{});
try installFile("res/lang/es.ini", lang_dir, ly_lang_path, "es.ini", .{}); try current_dir.copyFile("res/lang/es.ini", lang_dir, "es.ini", .{});
try installFile("res/lang/fr.ini", lang_dir, ly_lang_path, "fr.ini", .{}); try current_dir.copyFile("res/lang/fr.ini", lang_dir, "fr.ini", .{});
try installFile("res/lang/it.ini", lang_dir, ly_lang_path, "it.ini", .{}); try current_dir.copyFile("res/lang/it.ini", lang_dir, "it.ini", .{});
try installFile("res/lang/pl.ini", lang_dir, ly_lang_path, "pl.ini", .{}); try current_dir.copyFile("res/lang/pl.ini", lang_dir, "pl.ini", .{});
try installFile("res/lang/pt.ini", lang_dir, ly_lang_path, "pt.ini", .{}); try current_dir.copyFile("res/lang/pt.ini", lang_dir, "pt.ini", .{});
try installFile("res/lang/pt_BR.ini", lang_dir, ly_lang_path, "pt_BR.ini", .{}); try current_dir.copyFile("res/lang/pt_BR.ini", lang_dir, "pt_BR.ini", .{});
try installFile("res/lang/ro.ini", lang_dir, ly_lang_path, "ro.ini", .{}); try current_dir.copyFile("res/lang/ro.ini", lang_dir, "ro.ini", .{});
try installFile("res/lang/ru.ini", lang_dir, ly_lang_path, "ru.ini", .{}); try current_dir.copyFile("res/lang/ru.ini", lang_dir, "ru.ini", .{});
try installFile("res/lang/sr.ini", lang_dir, ly_lang_path, "sr.ini", .{}); try current_dir.copyFile("res/lang/sr.ini", lang_dir, "sr.ini", .{});
try installFile("res/lang/sv.ini", lang_dir, ly_lang_path, "sv.ini", .{}); try current_dir.copyFile("res/lang/sv.ini", lang_dir, "sv.ini", .{});
try installFile("res/lang/tr.ini", lang_dir, ly_lang_path, "tr.ini", .{}); try current_dir.copyFile("res/lang/tr.ini", lang_dir, "tr.ini", .{});
try installFile("res/lang/uk.ini", lang_dir, ly_lang_path, "uk.ini", .{}); try current_dir.copyFile("res/lang/uk.ini", lang_dir, "uk.ini", .{});
} }
{ {
const pam_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/pam.d" }); const pam_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/etc/pam.d" });
std.fs.cwd().makePath(pam_path) catch { std.fs.cwd().makePath(pam_path) catch {
if (!std.mem.eql(u8, dest_directory, "")) { if (!std.mem.eql(u8, dest_directory, "")) {
std.debug.print("warn: {s} already exists as a directory.\n", .{pam_path}); std.debug.print("warn: {s} already exists as a directory.\n", .{pam_path});
@@ -200,110 +231,34 @@ fn install_ly(allocator: std.mem.Allocator, patch_map: PatchMap, install_config:
var pam_dir = std.fs.cwd().openDir(pam_path, .{}) catch unreachable; var pam_dir = std.fs.cwd().openDir(pam_path, .{}) catch unreachable;
defer pam_dir.close(); defer pam_dir.close();
try installFile("res/pam.d/ly", pam_dir, pam_path, "ly", .{ .override_mode = 0o644 }); try current_dir.copyFile("res/pam.d/ly", pam_dir, "ly", .{ .override_mode = 0o644 });
} }
} }
fn install_service(allocator: std.mem.Allocator, patch_map: PatchMap) !void { pub fn uninstallall(step: *std.Build.Step, progress: ProgressNode) !void {
switch (init_system) { _ = progress;
.systemd => { try std.fs.cwd().deleteTree(data_directory);
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/lib/systemd/system" }); const allocator = step.owner.allocator;
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const patched_service = try patchFile(allocator, "res/ly.service", patch_map); const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/usr/bin/", exe_name });
try installText(patched_service, service_dir, service_path, "ly.service", .{ .mode = 0o644 }); try std.fs.cwd().deleteFile(exe_path);
},
.openrc => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const patched_service = try patchFile(allocator, "res/ly-openrc", patch_map); const pam_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/etc/pam.d/ly" });
try installText(patched_service, service_dir, service_path, executable_name, .{ .mode = 0o755 }); try std.fs.cwd().deleteFile(pam_path);
},
.runit => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/sv/ly" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const supervise_path = try std.fs.path.join(allocator, &[_][]const u8{ service_path, "supervise" }); const systemd_service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/usr/lib/systemd/system/ly.service" });
std.fs.cwd().deleteFile(systemd_service_path) catch {
std.debug.print("warn: systemd service not found.\n", .{});
};
const patched_conf = try patchFile(allocator, "res/ly-runit-service/conf", patch_map); const openrc_service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/etc/init.d/ly" });
try installText(patched_conf, service_dir, service_path, "conf", .{}); std.fs.cwd().deleteFile(openrc_service_path) catch {
std.debug.print("warn: openrc service not found.\n", .{});
};
try installFile("res/ly-runit-service/finish", service_dir, service_path, "finish", .{ .override_mode = 0o755 }); const runit_service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/etc/sv/ly" });
std.fs.cwd().deleteTree(runit_service_path) catch {
const patched_run = try patchFile(allocator, "res/ly-runit-service/run", patch_map); std.debug.print("warn: runit service not found.\n", .{});
try installText(patched_run, service_dir, service_path, "run", .{ .mode = 0o755 });
try std.fs.cwd().symLink("/run/runit/supervise.ly", supervise_path, .{});
std.debug.print("info: installed symlink /run/runit/supervise.ly\n", .{});
},
.s6 => {
const admin_service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/adminsv/default/contents.d" });
std.fs.cwd().makePath(admin_service_path) catch {};
var admin_service_dir = std.fs.cwd().openDir(admin_service_path, .{}) catch unreachable;
defer admin_service_dir.close();
const file = try admin_service_dir.createFile("ly-srv", .{});
file.close();
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/sv/ly-srv" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const patched_run = try patchFile(allocator, "res/ly-s6/run", patch_map);
try installText(patched_run, service_dir, service_path, "run", .{ .mode = 0o755 });
try installFile("res/ly-s6/type", service_dir, service_path, "type", .{});
},
.dinit => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/dinit.d" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const patched_service = try patchFile(allocator, "res/ly-dinit", patch_map);
try installText(patched_service, service_dir, service_path, "ly", .{});
},
}
}
pub fn Uninstaller(uninstall_config: bool) type {
return struct {
pub fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
const allocator = step.owner.allocator;
if (uninstall_config) {
try deleteTree(allocator, config_directory, "/ly", "ly config directory not found");
}
const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ prefix_directory, "/bin/", executable_name });
var success = true;
std.fs.cwd().deleteFile(exe_path) catch {
std.debug.print("warn: ly executable not found\n", .{});
success = false;
};
if (success) std.debug.print("info: deleted {s}\n", .{exe_path});
try deleteFile(allocator, config_directory, "/pam.d/ly", "ly pam file not found");
switch (init_system) {
.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"),
.runit => try deleteTree(allocator, config_directory, "/sv/ly", "runit service not found"),
.s6 => {
try deleteTree(allocator, config_directory, "/s6/sv/ly-srv", "s6 service not found");
try deleteFile(allocator, config_directory, "/s6/adminsv/default/contents.d/ly-srv", "s6 admin service not found");
},
.dinit => try deleteFile(allocator, config_directory, "/dinit.d/ly", "dinit service not found"),
}
}
}; };
} }
@@ -361,90 +316,3 @@ fn getVersionStr(b: *std.Build, name: []const u8, version: std.SemanticVersion)
}, },
} }
} }
fn installFile(
source_file: []const u8,
destination_directory: std.fs.Dir,
destination_directory_path: []const u8,
destination_file: []const u8,
options: std.fs.Dir.CopyFileOptions,
) !void {
try std.fs.cwd().copyFile(source_file, destination_directory, destination_file, options);
std.debug.print("info: installed {s}/{s}\n", .{ destination_directory_path, destination_file });
}
fn patchFile(allocator: std.mem.Allocator, source_file: []const u8, patch_map: PatchMap) ![]const u8 {
var file = try std.fs.cwd().openFile(source_file, .{});
defer file.close();
const reader = file.reader();
var text = try reader.readAllAlloc(allocator, std.math.maxInt(u16));
var iterator = patch_map.iterator();
while (iterator.next()) |kv| {
const new_text = try std.mem.replaceOwned(u8, allocator, text, kv.key_ptr.*, kv.value_ptr.*);
allocator.free(text);
text = new_text;
}
return text;
}
fn installText(
text: []const u8,
destination_directory: std.fs.Dir,
destination_directory_path: []const u8,
destination_file: []const u8,
options: std.fs.File.CreateFlags,
) !void {
var file = try destination_directory.createFile(destination_file, options);
defer file.close();
const writer = file.writer();
try writer.writeAll(text);
std.debug.print("info: installed {s}/{s}\n", .{ destination_directory_path, destination_file });
}
fn deleteFile(
allocator: std.mem.Allocator,
prefix: []const u8,
file: []const u8,
warning: []const u8,
) !void {
const path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix, file });
std.fs.cwd().deleteFile(path) catch |err| {
if (err == error.FileNotFound) {
std.debug.print("warn: {s}\n", .{warning});
return;
}
return err;
};
std.debug.print("info: deleted {s}\n", .{path});
}
fn deleteTree(
allocator: std.mem.Allocator,
prefix: []const u8,
directory: []const u8,
warning: []const u8,
) !void {
const path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix, directory });
var dir = std.fs.cwd().openDir(path, .{}) catch |err| {
if (err == error.FileNotFound) {
std.debug.print("warn: {s}\n", .{warning});
return;
}
return err;
};
dir.close();
try std.fs.cwd().deleteTree(path);
std.debug.print("info: deleted {s}\n", .{path});
}

View File

@@ -1,16 +1,15 @@
.{ .{
.name = .ly, .name = "ly",
.version = "1.1.2", .version = "1.0.0",
.fingerprint = 0xa148ffcc5dc2cb59, .minimum_zig_version = "0.12.0",
.minimum_zig_version = "0.14.0",
.dependencies = .{ .dependencies = .{
.clap = .{ .clap = .{
.url = "https://github.com/Hejsil/zig-clap/archive/refs/tags/0.10.0.tar.gz", .url = "https://github.com/Hejsil/zig-clap/archive/refs/tags/0.9.1.tar.gz",
.hash = "clap-0.10.0-oBajB434AQBDh-Ei3YtoKIRxZacVPF1iSwp3IX_ZB8f0", .hash = "122062d301a203d003547b414237229b09a7980095061697349f8bef41be9c30266b",
}, },
.zigini = .{ .zigini = .{
.url = "https://github.com/Kawaii-Ash/zigini/archive/2ed3d417f17fab5b0ee8cad8a63c6d62d7ac1042.tar.gz", .url = "https://github.com/Kawaii-Ash/zigini/archive/0bba97a12582928e097f4074cc746c43351ba4c8.tar.gz",
.hash = "zigini-0.3.1-BSkB7XJGAAB2E-sKyzhTaQCBlYBL8yqzE4E_jmSY99sC", .hash = "12209b971367b4066d40ecad4728e6fdffc4cc4f19356d424c2de57f5b69ac7a619a",
}, },
}, },
.paths = .{""}, .paths = .{""},

52
changelog.md Normal file
View File

@@ -0,0 +1,52 @@
# Zig Rewrite (Version 1.0.0)
## Config Options
res/config.ini contains all of the available config options and their default values.
### Additions
+ `border_fg` has been introduced to change the color of the borders.
+ `term_restore_cursor_cmd` should restore the cursor to it's usual state.
+ `vi_mode` to enable vi keybindings.
+ `sleep_key` and `sleep_cmd`.
+ `numlock` to set numlock on startup.
+ `initial_info_text` allows changing the initial text on the info line.
+ `box_title` to display a title at the top of the main box
Note: `sleep_cmd` is unset by default, meaning it's hidden and has no effect.
### Changes
+ xinitrc can be set to null to hide it.
+ `blank_password` has been renamed to `clear_password`.
+ `save_file` has been deprecated and will be removed in a future version.
### Removals
+ `wayland_specifier` has been removed.
## Save File
The save file is now in .ini format and stored in the same directory as the config.
Older save files will be migrated to the new format.
Example:
```ini
user = ash
session_index = 0
```
## Misc
+ Display server name added next to selected session.
+ getty@tty2 has been added as a conflict in res/ly.service, so if it is running, ly should still be able to start.
+ `XDG_CURRENT_DESKTOP` is now set by ly.
+ LANG is no longer set by ly.
+ X Server PID is fetched from /tmp/X{d}.lock to be able to kill the process since it detaches.
+ Non .desktop files are now ignored in sessions directory.
+ PAM auth is now done in a child process. (Fixes some issues with logging out and back in).
+ When ly receives SIGTERM, the terminal is now cleared and existing child processes are cleaned up.
+ Shift+Tab now focuses previous input.
+ Display text in the info line when authenticating.

File diff suppressed because it is too large Load Diff

183
readme.md
View File

@@ -1,22 +1,21 @@
# Ly - a TUI display manager # Ly - a TUI display manager
![Ly screenshot](https://user-images.githubusercontent.com/5473047/88958888-65efbf80-d2a1-11ea-8ae5-3f263bce9cce.png "Ly screenshot")
## Development is now continuing on [Codeberg](https://codeberg.org/AnErrupTion/ly), with the [GitHub](https://github.com/fairyglade/ly) repository becoming a mirror. Issues & pull requests on GitHub will be ignored from now on.
![Ly screenshot](.github/screenshot.png "Ly screenshot")
Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD. Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD.
## Dependencies ## Dependencies
- Compile-time: - Compile-time:
- zig 0.14.0 - zig 0.12.0 or 0.13.0
- libc - a C standard library
- pam - pam
- xcb (optional, required by default; needed for X11 support) - xcb
- Runtime (with default config): - Runtime (with default config):
- xorg - xorg
- xorg-xauth - xorg-xauth
- mcookie
- tput
- shutdown - shutdown
- brightnessctl
### Debian ### Debian
``` ```
@@ -28,15 +27,34 @@ Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD.
It is recommended to add a rule for Ly as it currently does not ship one. It is recommended to add a rule for Ly as it currently does not ship one.
``` ```
# dnf install kernel-devel pam-devel libxcb-devel zig # dnf install kernel-devel pam-devel libxcb-devel
``` ```
## Support ## Support
The following desktop environments were tested with success: The following desktop environments were tested with success:
- awesome
[Wayland Environments](#supported-wayland-environments) - bspwm
- budgie
[X11 Environments](#supported-x11-environments) - cinnamon
- deepin
- dwl
- dwm
- enlightenment
- gnome
- i3
- kde
- labwc
- lxde
- lxqt
- mate
- maxx
- pantheon
- qtile
- spectrwm
- sway
- windowmaker
- xfce
- xmonad
Ly should work with any X desktop environment, and provides Ly should work with any X desktop environment, and provides
basic wayland support (sway works very well, for example). basic wayland support (sway works very well, for example).
@@ -50,7 +68,7 @@ changing the source code won't be necessary :)
## Cloning and Compiling ## Cloning and Compiling
Clone the repository Clone the repository
``` ```
$ git clone https://codeberg.org/AnErrupTion/ly $ git clone https://github.com/fairyglade/ly
``` ```
Change the directory to ly Change the directory to ly
@@ -64,26 +82,14 @@ $ zig build
``` ```
Test in the configured tty (tty2 by default) Test in the configured tty (tty2 by default)
or a terminal emulator (but authentication won't work) or a terminal emulator (but desktop environments won't start)
``` ```
$ zig build run # zig build run
``` ```
**Important**: Running Ly in a terminal emulator as root is *not* recommended. If you Install Ly and the provided systemd service file
want to properly test Ly, please enable its service (as described below) and reboot
your machine.
Install Ly for systemd-based systems (the default)
``` ```
# zig build installexe # zig build installsystemd
```
Instead of DISPLAY_MANAGER you need to add your DM:
- gdm.service
- sddm.service
- lightdm.service
```
# systemctl disable DISPLAY_MANAGER
``` ```
Enable the service Enable the service
@@ -98,13 +104,13 @@ disable getty on Ly's tty to prevent "login" from spawning on top of it
``` ```
### OpenRC ### OpenRC
**NOTE 1**: On Gentoo, Ly will disable the `display-manager-init` service in order to run. **NOTE**: On Gentoo, Ly will disable the `display-manager-init` service in order to run.
Clone, compile and test. Clone, compile and test.
Install Ly and the provided OpenRC service Install Ly and the provided OpenRC service
``` ```
# zig build installexe -Dinit_system=openrc # zig build installopenrc
``` ```
Enable the service Enable the service
@@ -120,11 +126,9 @@ then you have to disable getty, so it doesn't respawn on top of ly
# rc-update del agetty.tty2 # rc-update del agetty.tty2
``` ```
**NOTE 2**: To avoid a console spawning on top on Ly, comment out the appropriate line from /etc/inittab (default is 2).
### runit ### runit
``` ```
# zig build installexe -Dinit_system=runit # zig build installrunit
# ln -s /etc/sv/ly /var/service/ # ln -s /etc/sv/ly /var/service/
``` ```
@@ -144,68 +148,24 @@ you should disable the agetty-tty2 service like this:
# rm /var/service/agetty-tty2 # rm /var/service/agetty-tty2
``` ```
### s6
```
# zig build installexe -Dinit_system=s6
```
Then, edit `/etc/s6/config/ttyX.conf` and set `SPAWN="no"`, where X is the TTY ID (e.g. `2`).
Finally, enable the service:
```
# s6-service add default ly-srv
# s6-db-reload
# s6-rc -u change ly-srv
```
### dinit
```
# zig build installexe -Dinit_system=dinit
# dinitctl enable ly
```
In addition to the steps above, you will also have to keep a TTY free within `/etc/dinit.d/config/console.conf`.
To do that, change `ACTIVE_CONSOLES` so that the tty that ly should use in `/etc/ly/config.ini` is free.
### Updating ### Updating
You can also install Ly without overrding the current configuration file. That's called You can also install Ly without copying the system service and the configuration file. That's
*updating*. To update, simply run: called *updating*. To update, simply run:
``` ```
# zig build installnoconf # zig build installnoconf
``` ```
You can, of course, still select the init system of your choice when using this command. If you want to also copy the default config file (but still not the system service), run:
```
# zig build installexe
```
## Arch Linux Installation ## Arch Linux Installation
You can install ly from the [`[extra]` repos](https://archlinux.org/packages/extra/x86_64/ly/): You can install ly from the [`[extra]` repos](https://archlinux.org/packages/extra/x86_64/ly/):
``` ```
# pacman -S ly $ sudo pacman -S ly
```
## Gentoo Installation
You can install ly from the GURU repository:
Note: If the package is masked, you may need to unmask it using ~amd64 keyword:
```bash
# echo 'x11-misc/ly ~amd64' >> /etc/portage/package.accept_keywords
```
1. Enable the GURU repository:
```bash
# eselect repository enable guru
```
2. Sync the GURU repository:
```bash
# emaint sync -r guru
```
3. Install ly from source:
```bash
# emerge --ask x11-misc/ly
``` ```
## Configuration ## Configuration
@@ -229,46 +189,19 @@ On Arch Linux, the example .xinitrc (/etc/X11/xinit/xinitrc) starts like this:
``` ```
## Tips ## Tips
- The numlock and capslock state is printed in the top-right corner. The numlock and capslock state is printed in the top-right corner.
- Use the F1 and F2 keys to respectively shutdown and reboot. Use the F1 and F2 keys to respectively shutdown and reboot.
- Take a look at your .xsession if X doesn't start, as it can interfere Take a look at your .xsession if X doesn't start, as it can interfere
(this file is launched with X to configure the display properly). (this file is launched with X to configure the display properly).
## Supported Wayland Environments
- budgie
- cosmic
- deepin
- enlightenment
- gnome
- hyprland
- kde
- labwc
- niri
- pantheon
- sway
- weston
## Supported X11 Environments
- awesome
- bspwm
- budgie
- cinnamon
- dwm
- enlightenment
- gnome
- kde
- leftwm
- lxde
- mate
- maxx
- pantheon
- qwm
- spectrwm
- windowmaker
- xfce
- xmonad
## PSX DOOM fire animation
To enable the famous PSX DOOM fire described by [Fabien Sanglard](http://fabiensanglard.net/doom_fire_psx/index.html),
just set `animation = doom` in `/etc/ly/config.ini`. You may also
disable the main box borders with `hide_borders = true`.
## Additional Information ## Additional Information
The name "Ly" is a tribute to the fairy from the game Rayman. The name "Ly" is a tribute to the fairy from the game Rayman.
Ly was tested by oxodao, who is some seriously awesome dude. Ly was tested by oxodao, who is some seriously awesome dude.
## Gentoo (OpenRC) installation tip
To avoid a console spawning on top on Ly, comment out the appropriate line from /etc/inittab (default is 2).

View File

@@ -1,167 +1,126 @@
# Ly supports 24-bit true color with styling, which means each color is a 32-bit value.
# The format is 0xSSRRGGBB, where SS is the styling, RR is red, GG is green, and BB is blue.
# Here are the possible styling options:
#define TB_BOLD 0x01000000
#define TB_UNDERLINE 0x02000000
#define TB_REVERSE 0x04000000
#define TB_ITALIC 0x08000000
#define TB_BLINK 0x10000000
#define TB_HI_BLACK 0x20000000
#define TB_BRIGHT 0x40000000
#define TB_DIM 0x80000000
# Programmatically, you'd apply them using the bitwise OR operator (|), but because Ly's
# configuration doesn't support using it, you have to manually compute the color value.
# Note that, if you want to use the default color value of the terminal, you can use the
# special value 0x00000000. This means that, if you want to use black, you *must* use
# the styling option TB_HI_BLACK (the RGB values are ignored when using this option).
# Allow empty password or not when authenticating
allow_empty_password = true
# The active animation # The active animation
# none -> Nothing # none -> Nothing (default)
# doom -> PSX DOOM fire # doom -> PSX DOOM fire
# matrix -> CMatrix # matrix -> CMatrix
# colormix -> Color mixing shader
animation = none animation = none
# Stop the animation after some time # Format string for clock in top right corner (see strftime specification). Example: %c
# 0 -> Run forever clock = null
# 1..2e12 -> Stop the animation after this many seconds
animation_timeout_sec = 0 # Enable/disable big clock
bigclock = false
# The character used to mask the password # The character used to mask the password
# You can either type it directly as a UTF-8 character (like *), or use a UTF-32
# codepoint (for example 0x2022 for a bullet point)
# If null, the password will be hidden
# Note: you can use a # by escaping it like so: \#
asterisk = * asterisk = *
# The number of failed authentications before a special animation is played... ;) # Erase password input on failure
auth_fails = 10 clear_password = false
# Enable vi keybindings
vi_mode = false
# The `fg` and `bg` color settings take a digit 0-8 corresponding to:
#define TB_DEFAULT 0x00
#define TB_BLACK 0x01
#define TB_RED 0x02
#define TB_GREEN 0x03
#define TB_YELLOW 0x04
#define TB_BLUE 0x05
#define TB_MAGENTA 0x06
#define TB_CYAN 0x07
#define TB_WHITE 0x08
#
# Setting both to zero makes `bg` black and `fg` white. To set the actual color palette you are encouraged to use another tool
# such as [mkinitcpio-colors](https://github.com/evanpurkhiser/mkinitcpio-colors). Note that the color palette defined with
# `mkinitcpio-colors` takes 16 colors (0-15), only values 0-8 are valid for `ly` config and these values do not correspond
# exactly. For instance, in defining palettes with `mkinitcpio-colors` the order is black, dark red, dark green, brown, dark
# blue, dark purple, dark cyan, light gray, dark gray, bright red, bright green, yellow, bright blue, bright purple, bright
# cyan, and white, indexed in that order 0 through 15. For example, the color defined for white (indexed at 15 in the mkinitcpio
# config) will be used by `ly` for `fg = 8`.
# Background color id # Background color id
bg = 0x00000000 bg = 0
# Change the state and language of the big clock # Foreground color id
# none -> Disabled (default) fg = 8
# en -> English
# fa -> Farsi # Border color
bigclock = none border_fg = 8
# Title to show at the top of the main box
box_title = null
# Initial text to show on the info line (Defaults to hostname)
initial_info_text = null
# Blank main box background # Blank main box background
# Setting to false will make it transparent # Setting to false will make it transparent
blank_box = true blank_box = true
# Border foreground color id
border_fg = 0x00FFFFFF
# Title to show at the top of the main box
# If set to null, none will be shown
box_title = null
# Brightness increase command
brightness_down_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q s 10%-
# Brightness decrease key, or null to disable
brightness_down_key = F5
# Brightness increase command
brightness_up_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q s +10%
# Brightness increase key, or null to disable
brightness_up_key = F6
# Erase password input on failure
clear_password = false
# Format string for clock in top right corner (see strftime specification). Example: %c
# If null, the clock won't be shown
clock = null
# CMatrix animation foreground color id
cmatrix_fg = 0x0000FF00
# CMatrix animation minimum codepoint. It uses a 16-bit integer
# For Japanese characters for example, you can use 0x3000 here
cmatrix_min_codepoint = 0x21
# CMatrix animation maximum codepoint. It uses a 16-bit integer
# For Japanese characters for example, you can use 0x30FF here
cmatrix_max_codepoint = 0x7B
# Color mixing animation first color id
colormix_col1 = 0x00FF0000
# Color mixing animation second color id
colormix_col2 = 0x000000FF
# Color mixing animation third color id
colormix_col3 = 0x20000000
# Console path
console_dev = /dev/console
# Input box active by default on startup
# Available inputs: info_line, session, login, password
default_input = login
# DOOM animation top color (low intensity flames)
doom_top_color = 0x00FF0000
# DOOM animation middle color (medium intensity flames)
doom_middle_color = 0x00FFFF00
# DOOM animation bottom color (high intensity flames)
doom_bottom_color = 0x00FFFFFF
# Error background color id
error_bg = 0x00000000
# Error foreground color id
# Default is red and bold
error_fg = 0x01FF0000
# Foreground color id
fg = 0x00FFFFFF
# Remove main box borders # Remove main box borders
hide_borders = false hide_borders = false
# Remove power management command hints # Main box margins
hide_key_hints = false margin_box_h = 2
margin_box_v = 1
# Initial text to show on the info line
# If set to null, the info line defaults to the hostname
initial_info_text = null
# Input boxes length # Input boxes length
input_len = 34 input_len = 34
# Active language # Max input sizes
# Available languages are found in $CONFIG_DIRECTORY/ly/lang/ max_desktop_len = 100
lang = en max_login_len = 255
max_password_len = 255
# Input box active by default on startup
# Available inputs: session, login, password
default_input = login
# Load the saved desktop and username # Load the saved desktop and username
load = true load = true
# Command executed when logging in # Save the current desktop and login as defaults
# If null, no command will be executed save = true
# Important: the code itself must end with `exec "$@"` in order to launch the session!
# You can also set environment variables in there, they'll persist until logout
login_cmd = null
# Command executed when logging out # Deprecated - Will be removed in a future version
# If null, no command will be executed # New save files are now loaded from the same directory as the config
# Important: the session will already be terminated when this command is executed, so # Currently used to migrate old save files to the new version
# no need to add `exec "$@"` at the end # File in which to save and load the default desktop and login
logout_cmd = null save_file = /etc/ly/save
# Main box horizontal margin # Remove power management command hints
margin_box_h = 2 hide_key_hints = false
# Main box vertical margin # Specifies the key used for shutdown (F1-F12)
margin_box_v = 1 shutdown_key = F1
# Specifies the key used for restart (F1-F12)
restart_key = F2
# Specifies the key used for sleep (F1-F12)
sleep_key = F3
# Command executed when pressing shutdown_key
shutdown_cmd = /sbin/shutdown -a now
# Command executed when pressing restart_key
restart_cmd = /sbin/shutdown -r now
# Command executed when pressing sleep key (can be null)
sleep_cmd = null
# Active language
# Available languages are found in /etc/ly/lang/
lang = en
# TTY in use
tty = 2
# Console path
console_dev = /dev/console
# Default path. If null, ly doesn't set a path.
path = /sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
# Event timeout in milliseconds # Event timeout in milliseconds
min_refresh_delta = 5 min_refresh_delta = 5
@@ -169,74 +128,35 @@ min_refresh_delta = 5
# Set numlock on/off at startup # Set numlock on/off at startup
numlock = false numlock = false
# Default path
# If null, ly doesn't set a path
path = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# Command executed when pressing restart_key
restart_cmd = /sbin/shutdown -r now
# Specifies the key used for restart (F1-F12)
restart_key = F2
# Save the current desktop and login as defaults
save = true
# Service name (set to ly to use the provided pam config file) # Service name (set to ly to use the provided pam config file)
service_name = ly service_name = ly
# Session log file path # Terminal reset command (tput is faster)
# This will contain stdout and stderr of Wayland sessions term_reset_cmd = /usr/bin/tput reset
# By default it's saved in the user's home directory
# Important: due to technical limitations, X11 and shell sessions aren't supported, which
# means you won't get any logs from those sessions
session_log = ly-session.log
# Setup command # Terminal restore cursor command
setup_cmd = $CONFIG_DIRECTORY/ly/setup.sh term_restore_cursor_cmd = /usr/bin/tput cnorm
# Command executed when pressing shutdown_key # Cookie generator
shutdown_cmd = /sbin/shutdown -a now mcookie_cmd = /usr/bin/mcookie
# Specifies the key used for shutdown (F1-F12) # Wayland setup command
shutdown_key = F1 wayland_cmd = /etc/ly/wsetup.sh
# Command executed when pressing sleep key (can be null)
sleep_cmd = null
# Specifies the key used for sleep (F1-F12)
sleep_key = F3
# Center the session name.
text_in_center = false
# TTY in use
tty = $DEFAULT_TTY
# Default vi mode
# normal -> normal mode
# insert -> insert mode
vi_default_mode = normal
# Enable vi keybindings
vi_mode = false
# Wayland desktop environments # Wayland desktop environments
# You can specify multiple directories, waylandsessions = /usr/share/wayland-sessions
# e.g. /usr/share/wayland-sessions:/usr/local/share/wayland-sessions
waylandsessions = $PREFIX_DIRECTORY/share/wayland-sessions
# Xorg server command # xinitrc (hidden if null)
x_cmd = $PREFIX_DIRECTORY/bin/X
# Xorg xauthority edition tool
xauth_cmd = $PREFIX_DIRECTORY/bin/xauth
# xinitrc
# If null, the xinitrc session will be hidden
xinitrc = ~/.xinitrc xinitrc = ~/.xinitrc
# Xorg server command
x_cmd = /usr/bin/X
# Xorg setup command
x_cmd_setup = /etc/ly/xsetup.sh
# Xorg xauthority edition tool
xauth_cmd = /usr/bin/xauth
# Xorg desktop environments # Xorg desktop environments
# You can specify multiple directories, xsessions = /usr/share/xsessions
# e.g. /usr/share/xsessions:/usr/local/share/xsessions
xsessions = $PREFIX_DIRECTORY/share/xsessions

View File

@@ -1,64 +0,0 @@
authenticating = جاري المصادقة...
brightness_down = خفض السطوع
brightness_up = رفع السطوع
capslock = capslock
err_alloc = فشل في تخصيص الذاكرة
err_bounds = out-of-bounds index
err_brightness_change = فشل في تغيير سطوع الشاشة
err_chdir = فشل في فتح مجلد المنزل
err_config = فشل في تفسير ملف الإعدادات
err_console_dev = فشل في الوصول إلى جهاز وحدة التحكم
err_dgn_oob = رسالة سجل (Log)
err_domain = اسم نطاق غير صالح
err_empty_password = لا يُسمح بكلمة مرور فارغة
err_envlist = فشل في جلب قائمة المتغيرات البيئية
err_hostname = فشل في جلب اسم المضيف (Hostname)
err_mlock = فشل في تأمين ذاكرة كلمة المرور (mlock)
err_null = مؤشر فارغ (Null pointer)
err_numlock = فشل في ضبط Num Lock
err_pam = فشل في معاملة PAM
err_pam_abort = تم إلغاء معاملة PAM
err_pam_acct_expired = الحساب منتهي الصلاحية
err_pam_auth = خطأ في المصادقة (Authentication error)
err_pam_authinfo_unavail = فشل في الحصول على معلومات المستخدم
err_pam_authok_reqd = انتهت صلاحية رمز المصادقة (Token)
err_pam_buf = خطأ في ذاكرة التخزين المؤقت (Buffer)
err_pam_cred_err = فشل في تعيين بيانات الاعتماد (Credentials)
err_pam_cred_expired = بيانات الاعتماد منتهية الصلاحية
err_pam_cred_insufficient = بيانات الاعتماد غير كافية
err_pam_cred_unavail = فشل في الحصول على بيانات الاعتماد
err_pam_maxtries = تم بلوغ الحد الأقصى لمحاولات المصادقة
err_pam_perm_denied = تم رفض الوصول (Permission denied)
err_pam_session = خطأ في جلسة المستخدم (Session error)
err_pam_sys = خطأ في النظام (System error)
err_pam_user_unknown = المستخدم غير موجود
err_path = فشل في تعيين متغير PATH
err_perm_dir = فشل في تغيير المجلد الحالي
err_perm_group = فشل في تخفيض صلاحيات المجموعة (Group permissions)
err_perm_user = فشل في تخفيض صلاحيات المستخدم (User permissions)
err_pwnam = فشل في جلب معلومات المستخدم
err_sleep = فشل في تنفيذ أمر sleep
err_tty_ctrl = فشل في نقل تحكم الطرفية (TTY)
err_unknown = حدث خطأ غير معروف
err_user_gid = فشل في تعيين معرّف المجموعة (GID) للمستخدم
err_user_init = فشل في تهيئة بيانات المستخدم
err_user_uid = فشل في تعيين معرّف المستخدم (UID)
err_xauth = فشل في تنفيذ أمر xauth
err_xcb_conn = فشل في الاتصال بمكتبة XCB
err_xsessions_dir = فشل في العثور على مجلد Xsessions
err_xsessions_open = فشل في فتح مجلد Xsessions
insert = ادخال
login = تسجيل الدخول
logout = تم تسجيل خروجك
no_x11_support = تم تعطيل دعم x11 اثناء وقت الـ compile
normal = عادي
numlock = numlock
other = اخر
password = كلمة السر
restart = اعادة التشغيل
shell = shell
shutdown = ايقاف التشغيل
sleep = وضع السكون
wayland = wayland
x11 = x11
xinitrc = xinitrc

View File

@@ -1,64 +1,45 @@
authenticating = autenticant...
brightness_down = abaixar brillantor
brightness_up = apujar brillantor
capslock = Bloq Majús capslock = Bloq Majús
err_alloc = assignació de memòria fallida err_alloc = falla d'assignació de memòria
err_bounds = índex fora de límits err_bounds = índex fora de límit
err_brightness_change = error en canviar la brillantor err_chdir = error al obrir carpeta home
err_chdir = error en obrir la carpeta home err_console_dev = error al accedir a la consola
err_console_dev = error en accedir a la consola
err_dgn_oob = missatge de registre err_dgn_oob = missatge de registre
err_domain = domini invàlid err_domain = domini invàlid
err_hostname = error al obtenir el nom del host
err_envlist = error en obtenir l'envlist err_mlock = error al bloquejar la clau de memòria
err_hostname = error en obtenir el nom de l'amfitrió
err_mlock = error en bloquejar la memòria de clau
err_null = punter nul err_null = punter nul
err_numlock = error en establir el Bloq num
err_pam = error en la transacció pam err_pam = error en la transacció pam
err_pam_abort = transacció pam avortada err_pam_abort = transacció pam avortada
err_pam_acct_expired = compte expirat err_pam_acct_expired = compte expirat
err_pam_auth = error d'autenticació err_pam_auth = error d'autenticació
err_pam_authinfo_unavail = error en obtenir la informació de l'usuari err_pam_authinfo_unavail = error al obtenir informació de l'usuari
err_pam_authok_reqd = token expirat err_pam_authok_reqd = token expirat
err_pam_buf = error en la memòria intermèdia err_pam_buf = error de la memòria intermitja
err_pam_cred_err = error en establir les credencials err_pam_cred_err = error al establir les credencials
err_pam_cred_expired = credencials expirades err_pam_cred_expired = credencials expirades
err_pam_cred_insufficient = credencials insuficients err_pam_cred_insufficient = credencials insuficients
err_pam_cred_unavail = error en obtenir credencials err_pam_cred_unavail = error al obtenir credencials
err_pam_maxtries = s'ha assolit al nombre màxim d'intents err_pam_maxtries = s'ha assolit al màxim nombre d'intents
err_pam_perm_denied = permís denegat err_pam_perm_denied = permís denegat
err_pam_session = error de sessió err_pam_session = error de sessió
err_pam_sys = error de sistema err_pam_sys = error de sistema
err_pam_user_unknown = usuari desconegut err_pam_user_unknown = usuari desconegut
err_path = error en establir la ruta err_path = error al establir la ruta
err_perm_dir = error en canviar el directori actual err_perm_dir = error al canviar de directori actual
err_perm_group = error en degradar els permisos de grup err_perm_group = error al degradar els permisos de grup
err_perm_user = error en degradar els permisos de l'usuari err_perm_user = error al degradar els permisos de l'usuari
err_pwnam = error en obtenir la informació de l'usuari err_pwnam = error al obtenir la informació de l'usuari
err_user_gid = error al establir el GID de l'usuari
err_user_init = error al inicialitzar usuari
err_unknown = ha ocorregut un error desconegut err_user_uid = error al establir el UID de l'usuari
err_user_gid = error en establir el GID de l'usuari err_xsessions_dir = error al cercar la carpeta de sessions
err_user_init = error en inicialitzar usuari err_xsessions_open = error al obrir la carpeta de sessions
err_user_uid = error en establir l'UID de l'usuari
err_xauth = error en la comanda xauth
err_xcb_conn = error en la connexió xcb
err_xsessions_dir = error en trobar la carpeta de sessions
err_xsessions_open = error en obrir la carpeta de sessions
insert = inserir
login = iniciar sessió login = iniciar sessió
logout = sessió tancada logout = tancar sessió
no_x11_support = el suport per x11 ha estat desactivat en la compilació
normal = normal
numlock = Bloq Num numlock = Bloq Num
password = Clau password = Clau
restart = reiniciar restart = reiniciar
shell = shell shell = shell
shutdown = aturar shutdown = aturar
sleep = suspendre
wayland = wayland wayland = wayland
x11 = x11
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -1,21 +1,13 @@
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_console_dev = chyba při přístupu do konzole err_console_dev = chyba při přístupu do konzole
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
err_pam = pam transakce selhala err_pam = pam transakce selhala
err_pam_abort = pam transakce přerušena err_pam_abort = pam transakce přerušena
err_pam_acct_expired = platnost účtu vypršela err_pam_acct_expired = platnost účtu vypršela
@@ -37,28 +29,17 @@ err_perm_dir = nepodařilo se změnit adresář
err_perm_group = nepodařilo se snížit skupinová oprávnění err_perm_group = nepodařilo se snížit skupinová oprávnění
err_perm_user = nepodařilo se snížit uživatelská oprávnění err_perm_user = nepodařilo se snížit uživatelská oprávnění
err_pwnam = nelze získat informace o uživateli 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
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
numlock = numlock numlock = numlock
password = heslo password = heslo
restart = restartovat restart = restartovat
shell = příkazový řádek shell = příkazový řádek
shutdown = vypnout shutdown = vypnout
wayland = wayland wayland = wayland
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -1,21 +1,13 @@
capslock = Feststelltaste capslock = Feststelltaste
err_alloc = Speicherzuweisung fehlgeschlagen err_alloc = Speicherzuweisung fehlgeschlagen
err_bounds = Listenindex ist außerhalb des Bereichs err_bounds = Listenindex ist außerhalb des Bereichs
err_chdir = Fehler beim oeffnen des home-ordners err_chdir = Fehler beim oeffnen des home-ordners
err_console_dev = Zugriff auf die Konsole fehlgeschlagen err_console_dev = Zugriff auf die Konsole fehlgeschlagen
err_dgn_oob = Protokoll Nachricht err_dgn_oob = Protokoll Nachricht
err_domain = Unzulaessige domain err_domain = Unzulaessige domain
err_hostname = Holen des Hostnames fehlgeschlagen err_hostname = Holen des Hostnames fehlgeschlagen
err_mlock = Abschließen des Passwortspeichers fehlgeschlagen err_mlock = Abschließen des Passwortspeichers fehlgeschlagen
err_null = Null Zeiger err_null = Null Zeiger
err_pam = pam Transaktion fehlgeschlagen err_pam = pam Transaktion fehlgeschlagen
err_pam_abort = pam Transaktion abgebrochen err_pam_abort = pam Transaktion abgebrochen
err_pam_acct_expired = Benutzerkonto abgelaufen err_pam_acct_expired = Benutzerkonto abgelaufen
@@ -37,28 +29,17 @@ err_perm_dir = Fehler beim wechseln des Ordners
err_perm_group = Fehler beim heruntersetzen der Gruppen Berechtigungen err_perm_group = Fehler beim heruntersetzen der Gruppen Berechtigungen
err_perm_user = Fehler beim heruntersetzen der Nutzer Berechtigungen err_perm_user = Fehler beim heruntersetzen der Nutzer Berechtigungen
err_pwnam = Holen der Benutzerinformationen fehlgeschlagen err_pwnam = Holen der Benutzerinformationen fehlgeschlagen
err_user_gid = Fehler beim setzen der Gruppen Id des Nutzers err_user_gid = Fehler beim setzen der Gruppen Id des Nutzers
err_user_init = Initialisierung des Nutzers fehlgeschlagen err_user_init = Initialisierung des Nutzers fehlgeschlagen
err_user_uid = Setzen der Benutzer Id fehlgeschlagen err_user_uid = Setzen der Benutzer Id fehlgeschlagen
err_xsessions_dir = Fehler beim finden des Sitzungsordners err_xsessions_dir = Fehler beim finden des Sitzungsordners
err_xsessions_open = Fehler beim öffnen des Sitzungsordners err_xsessions_open = Fehler beim öffnen des Sitzungsordners
login = Anmelden login = Anmelden
logout = Abgemeldet logout = Abgemeldet
numlock = Numtaste numlock = Numtaste
password = Passwort password = Passwort
restart = Neustarten restart = Neustarten
shell = shell shell = shell
shutdown = Herunterfahren shutdown = Herunterfahren
wayland = wayland wayland = wayland
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -1,21 +1,16 @@
authenticating = authenticating... authenticating = authenticating...
brightness_down = decrease brightness
brightness_up = increase brightness
capslock = capslock capslock = capslock
err_alloc = failed memory allocation err_alloc = failed memory allocation
err_bounds = out-of-bounds index err_bounds = out-of-bounds index
err_brightness_change = failed to change brightness
err_chdir = failed to open home folder err_chdir = failed to open home folder
err_config = unable to parse config file
err_console_dev = failed to access console err_console_dev = failed to access console
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_envlist = failed to get envlist err_envlist = failed to get envlist
err_hostname = failed to get hostname err_hostname = failed to get hostname
err_mcookie = mcookie command failed
err_mlock = failed to lock password memory err_mlock = failed to lock password memory
err_null = null pointer err_null = null pointer
err_numlock = failed to set numlock
err_pam = pam transaction failed err_pam = pam transaction failed
err_pam_abort = pam transaction aborted err_pam_abort = pam transaction aborted
err_pam_acct_expired = account expired err_pam_acct_expired = account expired
@@ -37,8 +32,6 @@ err_perm_dir = failed to change current directory
err_perm_group = failed to downgrade group permissions 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_tty_ctrl = tty control transfer failed
err_unknown = an unknown error occurred err_unknown = an unknown error occurred
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
@@ -50,15 +43,13 @@ err_xsessions_open = failed to open sessions folder
insert = insert insert = insert
login = login login = login
logout = logged out logout = logged out
no_x11_support = x11 support disabled at compile-time
normal = normal normal = normal
numlock = numlock numlock = numlock
other = other
password = password password = password
restart = reboot restart = reboot
shell = shell shell = shell
shutdown = shutdown shutdown = shutdown
sleep = sleep sleep = sleep
wayland = wayland wayland = wayland
x11 = x11
xinitrc = xinitrc xinitrc = xinitrc
x11 = x11

View File

@@ -1,21 +1,13 @@
authenticating = autenticando...
brightness_down = bajar brillo
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_console_dev = error al acceder a la consola err_console_dev = error al acceder a la consola
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
err_pam = error en la transacción pam err_pam = error en la transacción pam
err_pam_abort = transacción pam abortada err_pam_abort = transacción pam abortada
err_pam_acct_expired = cuenta expirada err_pam_acct_expired = cuenta expirada
@@ -37,28 +29,17 @@ err_perm_dir = error al cambiar el directorio actual
err_perm_group = error al degradar los permisos del grupo err_perm_group = error al degradar los permisos del grupo
err_perm_user = error al degradar los permisos del usuario err_perm_user = error al degradar los permisos del usuario
err_pwnam = error al obtener la información del usuario 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
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 login = iniciar sesión
login = usuario
logout = cerrar sesión logout = cerrar sesión
no_x11_support = soporte para x11 deshabilitado en tiempo de compilación
normal = normal
numlock = Bloq Num numlock = Bloq Num
other = otro
password = contraseña password = contraseña
restart = reiniciar restart = reiniciar
shell = shell shell = shell
shutdown = apagar shutdown = apagar
sleep = suspender
wayland = wayland wayland = wayland
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -1,27 +1,19 @@
authenticating = authentification... capslock = verr.maj
brightness_down = diminuer la luminosité
brightness_up = augmenter la luminosité
capslock = verr.maj
err_alloc = échec d'allocation mémoire err_alloc = échec d'allocation mémoire
err_bounds = indice hors-limite err_bounds = indice hors-limite
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_config = échec de lecture du fichier de configuration
err_console_dev = échec d'accès à la console err_console_dev = échec d'accès à la console
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_envlist = échec de lecture de la liste d'environnement
err_hostname = échec de lecture du nom d'hôte err_hostname = échec de lecture du nom d'hôte
err_mlock = échec du verrouillage mémoire err_mlock = échec du verrouillage mémoire
err_null = pointeur null err_null = pointeur null
err_numlock = échec de modification du verr.num
err_pam = échec de la transaction pam err_pam = échec de la transaction pam
err_pam_abort = transaction pam avortée err_pam_abort = transaction pam avortée
err_pam_acct_expired = compte expiré err_pam_acct_expired = compte expiré
err_pam_auth = erreur d'authentification err_pam_auth = erreur d'authentification
err_pam_authinfo_unavail = échec de l'obtention des infos utilisateur
err_pam_authok_reqd = tiquet expiré err_pam_authok_reqd = tiquet expiré
err_pam_authinfo_unavail = échec de l'obtention des infos utilisateur
err_pam_buf = erreur de mémoire tampon err_pam_buf = erreur de mémoire tampon
err_pam_cred_err = échec de la modification des identifiants err_pam_cred_err = échec de la modification des identifiants
err_pam_cred_expired = identifiants expirés err_pam_cred_expired = identifiants expirés
@@ -37,28 +29,17 @@ err_perm_dir = échec de changement de répertoire
err_perm_group = échec du déclassement des permissions de groupe 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_tty_ctrl = échec du transfert de contrôle du terminal
err_unknown = une erreur inconnue est survenue
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
err_xauth = échec de la commande xauth
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
insert = insertion
login = identifiant login = identifiant
logout = déconnecté logout = déconnection
no_x11_support = support pour x11 désactivé lors de la compilation
normal = normal
numlock = verr.num numlock = verr.num
other = autre
password = mot de passe password = mot de passe
restart = redémarrer restart = redémarrer
shell = shell shell = shell
shutdown = éteindre shutdown = éteindre
sleep = veille
wayland = wayland wayland = wayland
x11 = x11
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -1,21 +1,13 @@
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_console_dev = impossibile aprire console err_console_dev = impossibile aprire console
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
err_pam = transazione PAM fallita err_pam = transazione PAM fallita
err_pam_abort = transazione PAM interrotta err_pam_abort = transazione PAM interrotta
err_pam_acct_expired = account scaduto err_pam_acct_expired = account scaduto
@@ -37,28 +29,17 @@ err_perm_dir = impossibile cambiare directory corrente
err_perm_group = impossibile ridurre permessi gruppo err_perm_group = impossibile ridurre permessi gruppo
err_perm_user = impossibile ridurre permessi utente err_perm_user = impossibile ridurre permessi utente
err_pwnam = impossibile ottenere dati utente 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
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
numlock = numlock numlock = numlock
password = password password = password
restart = riavvio restart = riavvio
shell = shell shell = shell
shutdown = arresto shutdown = arresto
wayland = wayland wayland = wayland
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -1,49 +0,0 @@
#!/usr/bin/env python3
from pathlib import Path
from sys import stderr
def process_lang_file(path: Path, lang_keys: list[str]) -> None:
# read key-value-pairs from lang file into dict
existing_entries = {}
with open(path, "r", encoding="UTF-8") as fh:
while line := fh.readline():
try:
key, value = line.split("=", 1)
existing_entries[key.strip()] = value.strip()
except ValueError: # line does not contain '='
continue
# re-write current lang file with entries in order of occurence in `lang_keys`
# and with empty lines for missing translations
with open(path, "w", encoding="UTF-8") as fh:
for item in lang_keys:
try:
fh.write(f"{item} = {existing_entries[item]}\n")
except KeyError: # no translation for `item` yet
fh.write("\n")
def main() -> None:
zig_lang_file = Path(__file__).parent.joinpath("../../src/config/Lang.zig").resolve()
if not zig_lang_file.exists():
print(f"ERROR: File '{zig_lang_file.as_posix()}' does not exist. Exiting.", file=stderr)
exit(1)
# read "language keys" from `zig_lang_file` into list
lang_keys = []
with open(zig_lang_file, "r", encoding="UTF-8") as fh:
while line := fh.readline():
# only process lines that are not empty or no comments
if not (line.strip() == "" or line.startswith("//")):
lang_keys.append(line.split(":")[0].strip())
lang_files = [f for f in Path.iterdir(Path(__file__).parent) if f.name.endswith(".ini") and f.is_file()]
for file in lang_files:
process_lang_file(file, lang_keys)
if __name__ == "__main__":
main()

View File

@@ -1,64 +1,45 @@
authenticating = uwierzytelnianie...
brightness_down = zmniejsz jasność
brightness_up = zwiększ jasność
capslock = capslock capslock = capslock
err_alloc = nieudana alokacja pamięci err_alloc = nieudana alokacja pamięci
err_bounds = indeks poza zakresem err_bounds = indeks poza granicami
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_config = nie można przetworzyć pliku konfiguracyjnego err_console_dev = nie udało się uzyskać dostępu do konsoli
err_console_dev = nie udało się uzyskać dostępu do konsoli
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_envlist = nie udało się pobrać listy zmiennych środowiskowych
err_hostname = nie udało się uzyskać nazwy hosta err_hostname = nie udało się uzyskać nazwy hosta
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 = wskaźnik zerowy
err_numlock = nie udało się ustawić numlock
err_pam = transakcja pam nieudana err_pam = transakcja pam nieudana
err_pam_abort = transakcja pam przerwana err_pam_abort = transakcja pam przerwana
err_pam_acct_expired = konto wygasło err_pam_acct_expired = konto wygasło
err_pam_auth = błąd uwierzytelniania err_pam_auth = błąd autentyfikacji
err_pam_authinfo_unavail = nie udało się zdobyć informacji o użytkowniku err_pam_authinfo_unavail = nie udało się zdobyć informacji o użytkowniku
err_pam_authok_reqd = token wygasł err_pam_authok_reqd = token wygasł
err_pam_buf = błąd bufora pamięci err_pam_buf = błąd bufora pamięci
err_pam_cred_err = nie udało się ustawić uwierzytelnienia err_pam_cred_err = nie udało się ustawić uwierzytelnienia
err_pam_cred_expired = uwierzytelnienie wygasło err_pam_cred_expired = uwierzytelnienie wygasło
err_pam_cred_insufficient = niewystarczające uwierzytelnienie err_pam_cred_insufficient = niewystarczające uwierzytelnienie
err_pam_cred_unavail = nie udało się uzyskać uwierzytelnienia err_pam_cred_unavail = nie udało się uzyskać uwierzytelnienia
err_pam_maxtries = osiągnięto limit prób err_pam_maxtries = osiągnięto limit prób
err_pam_perm_denied = odmowa dostępu err_pam_perm_denied = brak uprawnień
err_pam_session = błąd sesji err_pam_session = błąd sesji
err_pam_sys = błąd systemu err_pam_sys = błąd systemu
err_pam_user_unknown = nieznany użytkownik err_pam_user_unknown = nieznany użytkownik
err_path = nie udało się ustawić ścieżki err_path = nie udało się ustawić ścieżki
err_perm_dir = nie udało się zmienić obecnego katalogu err_perm_dir = nie udało się zmienić obecnego katalogu
err_perm_group = nie udało się obniżyć uprawnień grupy err_perm_group = nie udało się obniżyć uprawnień grupy
err_perm_user = nie udało się obniżyć uprawnień użytkownika 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_tty_ctrl = nie udało się przekazać kontroli tty
err_unknown = wystąpił nieznany błąd
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
err_user_uid = nie udało się ustawić UID użytkownika err_user_uid = nie udało się ustawić UID użytkownika
err_xauth = polecenie xauth nie powiodło się err_xsessions_dir = nie udało się znaleźć folderu sesji
err_xcb_conn = połączenie xcb nie powiodło się err_xsessions_open = nie udało się otworzyć folderu sesji
err_xsessions_dir = nie udało się znaleźć folderu sesji
err_xsessions_open = nie udało się otworzyć folderu sesji
insert = wstaw
login = login login = login
logout = wylogowano logout = wylogowano
no_x11_support = wsparcie X11 wyłączone podczas kompilacji
normal = normalny
numlock = numlock numlock = numlock
other = inny
password = hasło password = hasło
restart = uruchom ponownie restart = uruchom ponownie
shell = powłoka shell = powłoka
shutdown = wyłącz shutdown = wyłącz
sleep = uśpij
wayland = wayland wayland = wayland
x11 = x11
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -1,21 +1,13 @@
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_console_dev = erro ao aceder à consola err_console_dev = erro ao aceder à consola
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
err_pam = erro na transação pam err_pam = erro na transação pam
err_pam_abort = transação pam abortada err_pam_abort = transação pam abortada
err_pam_acct_expired = conta expirada err_pam_acct_expired = conta expirada
@@ -37,28 +29,17 @@ err_perm_dir = erro ao alterar o diretório atual
err_perm_group = erro ao reduzir as permissões do grupo err_perm_group = erro ao reduzir as permissões do grupo
err_perm_user = erro ao reduzir as permissões do utilizador err_perm_user = erro ao reduzir as permissões do utilizador
err_pwnam = erro ao obter informação do utilizador 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
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
numlock = numlock numlock = numlock
password = palavra-passe password = palavra-passe
restart = reiniciar restart = reiniciar
shell = shell shell = shell
shutdown = encerrar shutdown = encerrar
wayland = wayland wayland = wayland
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -1,21 +1,13 @@
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_console_dev = não foi possível acessar o console err_console_dev = não foi possível acessar o console
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
err_pam = transação pam malsucedida err_pam = transação pam malsucedida
err_pam_abort = transação pam abortada err_pam_abort = transação pam abortada
err_pam_acct_expired = conta expirada err_pam_acct_expired = conta expirada
@@ -37,28 +29,17 @@ err_perm_dir = não foi possível alterar o diretório atual
err_perm_group = não foi possível reduzir as permissões de grupo err_perm_group = não foi possível reduzir as permissões de grupo
err_perm_user = não foi possível reduzir as permissões de usuário err_perm_user = não foi possível reduzir as permissões de usuário
err_pwnam = não foi possível obter informações do usuário err_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
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
numlock = numlock numlock = numlock
password = senha password = senha
restart = reiniciar restart = reiniciar
shell = shell shell = shell
shutdown = desligar shutdown = desligar
wayland = wayland wayland = wayland
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -1,12 +1,7 @@
capslock = capslock capslock = capslock
err_console_dev = nu s-a putut accesa consola err_console_dev = nu s-a putut accesa consola
@@ -14,9 +9,6 @@ err_console_dev = nu s-a putut accesa consola
err_pam_abort = tranzacţie pam anulată err_pam_abort = tranzacţie pam anulată
err_pam_acct_expired = cont expirat err_pam_acct_expired = cont expirat
err_pam_auth = eroare de autentificare err_pam_auth = eroare de autentificare
@@ -42,23 +34,12 @@ err_perm_user = nu s-a putut face downgrade permisiunilor de utilizator
login = utilizator login = utilizator
logout = opreşte sesiunea logout = opreşte sesiunea
numlock = numlock numlock = numlock
password = parolă password = parolă
restart = resetează restart = resetează
shell = shell shell = shell
shutdown = opreşte sistemul shutdown = opreşte sistemul
wayland = wayland wayland = wayland
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -1,21 +1,13 @@
capslock = capslock capslock = capslock
err_alloc = не удалось выделить память err_alloc = не удалось выделить память
err_bounds = за пределами индекса err_bounds = за пределами индекса
err_chdir = не удалось открыть домашнюю папку err_chdir = не удалось открыть домашнюю папку
err_console_dev = не удалось получить доступ к консоли err_console_dev = не удалось получить доступ к консоли
err_dgn_oob = отладочное сообщение (log) err_dgn_oob = отладочное сообщение (log)
err_domain = неверный домен err_domain = неверный домен
err_hostname = не удалось получить имя хоста err_hostname = не удалось получить имя хоста
err_mlock = сбой блокировки памяти err_mlock = сбой блокировки памяти
err_null = нулевой указатель err_null = нулевой указатель
err_pam = pam транзакция не удалась err_pam = pam транзакция не удалась
err_pam_abort = pam транзакция прервана err_pam_abort = pam транзакция прервана
err_pam_acct_expired = срок действия аккаунта истёк err_pam_acct_expired = срок действия аккаунта истёк
@@ -37,28 +29,17 @@ err_perm_dir = не удалось изменить текущий катало
err_perm_group = не удалось понизить права доступа группы err_perm_group = не удалось понизить права доступа группы
err_perm_user = не удалось понизить права доступа пользователя err_perm_user = не удалось понизить права доступа пользователя
err_pwnam = не удалось получить информацию о пользователе err_pwnam = не удалось получить информацию о пользователе
err_user_gid = не удалось установить GID пользователя err_user_gid = не удалось установить GID пользователя
err_user_init = не удалось инициализировать пользователя err_user_init = не удалось инициализировать пользователя
err_user_uid = не удалось установить UID пользователя err_user_uid = не удалось установить UID пользователя
err_xsessions_dir = не удалось найти сессионную папку err_xsessions_dir = не удалось найти сессионную папку
err_xsessions_open = не удалось открыть сессионную папку err_xsessions_open = не удалось открыть сессионную папку
login = логин login = логин
logout = logged out logout = logged out
numlock = numlock numlock = numlock
password = пароль password = пароль
restart = перезагрузить restart = перезагрузить
shell = shell shell = shell
shutdown = выключить shutdown = выключить
wayland = wayland wayland = wayland
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -1,21 +1,13 @@
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_console_dev = neuspijesno pristupanje konzoli err_console_dev = neuspijesno pristupanje konzoli
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
err_pam = pam transakcija neuspijesna err_pam = pam transakcija neuspijesna
err_pam_abort = pam transakcija prekinuta err_pam_abort = pam transakcija prekinuta
err_pam_acct_expired = nalog istekao err_pam_acct_expired = nalog istekao
@@ -33,32 +25,21 @@ err_pam_session = greska sesije
err_pam_sys = greska sistema err_pam_sys = greska sistema
err_pam_user_unknown = nepoznat korisnik err_pam_user_unknown = nepoznat korisnik
err_path = neuspjelo postavljanje path-a err_path = neuspjelo postavljanje path-a
err_perm_dir = neuspjelo mijenjanje foldera err_perm_dir = neuspjelo mijenjanje foldera
err_perm_group = neuspjesno snizavanje dozvola grupe err_perm_group = neuspjesno snizavanje dozvola grupe
err_perm_user = neuspijesno snizavanje dozvola korisnika err_perm_user = neuspijesno snizavanje dozvola korisnika
err_pwnam = neuspijesno skupljanje informacija o korisniku 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
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
numlock = numlock numlock = numlock
password = lozinka password = lozinka
restart = ponovo pokreni restart = ponovo pokreni
shell = shell shell = shell
shutdown = ugasi shutdown = ugasi
wayland = wayland wayland = wayland
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -1,21 +1,13 @@
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_console_dev = misslyckades att komma åt konsol err_console_dev = misslyckades att komma åt konsol
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
err_pam = pam-transaktion misslyckades err_pam = pam-transaktion misslyckades
err_pam_abort = pam-transaktion avbröts err_pam_abort = pam-transaktion avbröts
err_pam_acct_expired = konto upphört err_pam_acct_expired = konto upphört
@@ -37,28 +29,17 @@ err_perm_dir = misslyckades att ändra aktuell katalog
err_perm_group = misslyckades att nergradera gruppbehörigheter err_perm_group = misslyckades att nergradera gruppbehörigheter
err_perm_user = misslyckades att nergradera användarbehörigheter err_perm_user = misslyckades att nergradera användarbehörigheter
err_pwnam = misslyckades att hämta användarinfo 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
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
numlock = numlock numlock = numlock
password = lösenord password = lösenord
restart = starta om restart = starta om
shell = skal shell = skal
shutdown = stäng av shutdown = stäng av
wayland = wayland wayland = wayland
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -1,21 +1,13 @@
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_console_dev = konsola erisilemedi err_console_dev = konsola erisilemedi
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
err_pam = pam islemi basarisiz oldu err_pam = pam islemi basarisiz oldu
err_pam_abort = pam islemi durduruldu err_pam_abort = pam islemi durduruldu
err_pam_acct_expired = hesabin suresi dolmus err_pam_acct_expired = hesabin suresi dolmus
@@ -37,28 +29,17 @@ err_perm_dir = gecerli dizin degistirilemedi
err_perm_group = grup izinleri dusurulemedi err_perm_group = grup izinleri dusurulemedi
err_perm_user = kullanici izinleri dusurulemedi err_perm_user = kullanici izinleri dusurulemedi
err_pwnam = kullanici bilgileri alinamadi 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
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
numlock = numlock numlock = numlock
password = sifre password = sifre
restart = yeniden baslat restart = yeniden baslat
shell = shell shell = shell
shutdown = makineyi kapat shutdown = makineyi kapat
wayland = wayland wayland = wayland
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -1,21 +1,13 @@
capslock = capslock capslock = capslock
err_alloc = невдале виділення пам'яті err_alloc = невдале виділення пам'яті
err_bounds = поза межами індексу err_bounds = поза межами індексу
err_chdir = не вдалося відкрити домашній каталог err_chdir = не вдалося відкрити домашній каталог
err_console_dev = невдалий доступ до консолі err_console_dev = невдалий доступ до консолі
err_dgn_oob = повідомлення журналу (log) err_dgn_oob = повідомлення журналу (log)
err_domain = недійсний домен err_domain = недійсний домен
err_hostname = не вдалося отримати ім'я хосту err_hostname = не вдалося отримати ім'я хосту
err_mlock = збій блокування пам'яті err_mlock = збій блокування пам'яті
err_null = нульовий вказівник err_null = нульовий вказівник
err_pam = невдала pam транзакція err_pam = невдала pam транзакція
err_pam_abort = pam транзакція перервана err_pam_abort = pam транзакція перервана
err_pam_acct_expired = термін дії акаунту вичерпано err_pam_acct_expired = термін дії акаунту вичерпано
@@ -37,28 +29,17 @@ err_perm_dir = не вдалося змінити поточний катало
err_perm_group = не вдалося понизити права доступу групи err_perm_group = не вдалося понизити права доступу групи
err_perm_user = не вдалося понизити права доступу користувача err_perm_user = не вдалося понизити права доступу користувача
err_pwnam = не вдалося отримати дані користувача err_pwnam = не вдалося отримати дані користувача
err_user_gid = не вдалося змінити GID користувача err_user_gid = не вдалося змінити GID користувача
err_user_init = не вдалося ініціалізувати користувача err_user_init = не вдалося ініціалізувати користувача
err_user_uid = не вдалося змінити UID користувача err_user_uid = не вдалося змінити UID користувача
err_xsessions_dir = не вдалося знайти каталог сесій err_xsessions_dir = не вдалося знайти каталог сесій
err_xsessions_open = не вдалося відкрити каталог сесій err_xsessions_open = не вдалося відкрити каталог сесій
login = логін login = логін
logout = вийти logout = вийти
numlock = numlock numlock = numlock
password = пароль password = пароль
restart = перезавантажити restart = перезавантажити
shell = оболонка shell = оболонка
shutdown = вимкнути shutdown = вимкнути
wayland = wayland wayland = wayland
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -1,64 +0,0 @@
capslock = 大写锁定
err_alloc = 内存分配失败
err_bounds = 索引越界
err_chdir = 无法打开home文件夹
err_console_dev = 无法访问控制台
err_dgn_oob = 日志消息
err_domain = 无效的域
err_hostname = 获取主机名失败
err_mlock = 锁定密码存储器失败
err_null = 空指针
err_pam = PAM事件失败
err_pam_abort = PAM事务已中止
err_pam_acct_expired = 帐户已过期
err_pam_auth = 身份验证错误
err_pam_authinfo_unavail = 获取用户信息失败
err_pam_authok_reqd = 口令已过期
err_pam_buf = 内存缓冲区错误
err_pam_cred_err = 设置凭据失败
err_pam_cred_expired = 凭据已过期
err_pam_cred_insufficient = 凭据不足
err_pam_cred_unavail = 无法获取凭据
err_pam_maxtries = 已达到最大尝试次数限制
err_pam_perm_denied = 拒绝访问
err_pam_session = 会话错误
err_pam_sys = 系统错误
err_pam_user_unknown = 未知用户
err_path = 无法设置路径
err_perm_dir = 更改当前目录失败
err_perm_group = 组权限降级失败
err_perm_user = 用户权限降级失败
err_pwnam = 获取用户信息失败
err_user_gid = 设置用户GID失败
err_user_init = 初始化用户失败
err_user_uid = 设置用户UID失败
err_xsessions_dir = 找不到会话文件夹
err_xsessions_open = 无法打开会话文件夹
login = 登录
logout = 注销
numlock = 数字锁定
password = 密码
shell = shell
wayland = wayland
x11 = x11
xinitrc = xinitrc

View File

@@ -1,8 +0,0 @@
type = process
restart = true
smooth-recovery = true
command = $PREFIX_DIRECTORY/bin/$EXE_NAME
depends-on = loginready
termsignal = HUP
# ly needs access to the console while loginready already occupies it
options = shares-console

View File

@@ -20,16 +20,16 @@ then
fi fi
## Get the tty from the conf file ## Get the tty from the conf file
CONFTTY=$(cat $CONFIG_DIRECTORY/ly/config.ini | sed -n 's/^tty.*=[^1-9]*// p') CONFTTY=$(cat /etc/ly/config.ini | sed -n 's/^tty.*=[^1-9]*// p')
## The execution vars ## The execution vars
# If CONFTTY is empty then default to $DEFAULT_TTY # If CONFTTY is empty then default to 2
TTY="tty${CONFTTY:-$DEFAULT_TTY}" TTY="tty${CONFTTY:-2}"
TERM=linux TERM=linux
BAUD=38400 BAUD=38400
# If we don't have getty then we should have agetty # If we don't have getty then we should have agetty
command=${commandB:-$commandUL} command=${commandB:-$commandUL}
command_args_foreground="-nl $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME $TTY $BAUD $TERM" command_args_foreground="-nl /usr/bin/ly $TTY $BAUD $TERM"
depend() { depend() {
after agetty after agetty

View File

@@ -8,5 +8,5 @@ fi
BAUD_RATE=38400 BAUD_RATE=38400
TERM_NAME=linux TERM_NAME=linux
auxtty=$(/bin/cat $CONFIG_DIRECTORY/ly/config.ini 2>/dev/null 1| /bin/sed -n 's/\(^[[:space:]]*tty[[:space:]]*=[[:space:]]*\)\([[:digit:]][[:digit:]]*\)\(.*\)/\2/p') auxtty=$(/bin/cat /etc/ly/config.ini 2>/dev/null 1| /bin/sed -n 's/\(^[[:space:]]*tty[[:space:]]*=[[:space:]]*\)\([[:digit:]][[:digit:]]*\)\(.*\)/\2/p')
TTY=tty${auxtty:-$DEFAULT_TTY} TTY=tty${auxtty:-2}

View File

@@ -10,4 +10,4 @@ elif [ -x /sbin/agetty -o -x /bin/agetty ]; then
GETTY=agetty GETTY=agetty
fi fi
exec setsid ${GETTY} ${GETTY_ARGS} -nl $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME "${TTY}" "${BAUD_RATE}" "${TERM_NAME}" exec setsid ${GETTY} ${GETTY_ARGS} -nl /usr/bin/ly "${TTY}" "${BAUD_RATE}" "${TERM_NAME}"

View File

@@ -1,2 +0,0 @@
#!/bin/execlineb -P
exec agetty -L -8 -n -l $PREFIX_DIRECTORY/bin/$EXE_NAME tty$DEFAULT_TTY 115200

View File

@@ -1 +0,0 @@
longrun

View File

@@ -1,14 +1,14 @@
[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@tty2.service
Conflicts=getty@tty$DEFAULT_TTY.service Conflicts=getty@tty2.service
[Service] [Service]
Type=idle Type=idle
ExecStart=$PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME ExecStart=/usr/bin/ly
StandardInput=tty StandardInput=tty
TTYPath=/dev/tty$DEFAULT_TTY TTYPath=/dev/tty2
TTYReset=yes TTYReset=yes
TTYVHangup=yes TTYVHangup=yes

View File

@@ -1,16 +1,6 @@
#%PAM-1.0 #%PAM-1.0
auth include login auth include login
-auth optional pam_gnome_keyring.so
-auth optional pam_kwallet5.so
account include login account include login
password 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 include login
-session optional pam_gnome_keyring.so auto_start
-session optional pam_kwallet5.so auto_start

View File

@@ -1,107 +0,0 @@
#!/bin/sh
# Shell environment setup after login
# Copyright (C) 2015-2016 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
# This file is extracted from kde-workspace (kdm/kfrontend/genkdmconf.c)
# Copyright (C) 2001-2005 Oswald Buddenhagen <ossi@kde.org>
# Copyright (C) 2024 The Fairy Glade
# This work is free. You can redistribute it and/or modify it under the
# terms of the Do What The Fuck You Want To Public License, Version 2,
# as published by Sam Hocevar. See the LICENSE file for more details.
# Note that the respective logout scripts are not sourced.
case $SHELL in
*/bash)
[ -z "$BASH" ] && exec $SHELL "$0" "$@"
set +o posix
[ -f "$CONFIG_DIRECTORY"/profile ] && . "$CONFIG_DIRECTORY"/profile
if [ -f "$HOME"/.bash_profile ]; then
. "$HOME"/.bash_profile
elif [ -f "$HOME"/.bash_login ]; then
. "$HOME"/.bash_login
elif [ -f "$HOME"/.profile ]; then
. "$HOME"/.profile
fi
;;
*/zsh)
[ -z "$ZSH_NAME" ] && exec $SHELL "$0" "$@"
[ -d "$CONFIG_DIRECTORY"/zsh ] && zdir="$CONFIG_DIRECTORY"/zsh || zdir="$CONFIG_DIRECTORY"
zhome=${ZDOTDIR:-"$HOME"}
# zshenv is always sourced automatically.
[ -f "$zdir"/zprofile ] && . "$zdir"/zprofile
[ -f "$zhome"/.zprofile ] && . "$zhome"/.zprofile
[ -f "$zdir"/zlogin ] && . "$zdir"/zlogin
[ -f "$zhome"/.zlogin ] && . "$zhome"/.zlogin
emulate -R sh
;;
*/csh|*/tcsh)
# [t]cshrc is always sourced automatically.
# Note that sourcing csh.login after .cshrc is non-standard.
sess_tmp=$(mktemp /tmp/sess-env-XXXXXX)
$SHELL -c "if (-f $CONFIG_DIRECTORY/csh.login) source $CONFIG_DIRECTORY/csh.login; if (-f ~/.login) source ~/.login; /bin/sh -c 'export -p' >! $sess_tmp"
. "$sess_tmp"
rm -f "$sess_tmp"
;;
*/fish)
[ -f "$CONFIG_DIRECTORY"/profile ] && . "$CONFIG_DIRECTORY"/profile
[ -f "$HOME"/.profile ] && . "$HOME"/.profile
sess_tmp=$(mktemp /tmp/sess-env-XXXXXX)
$SHELL --login -c "/bin/sh -c 'export -p' > $sess_tmp"
. "$sess_tmp"
rm -f "$sess_tmp"
;;
*) # Plain sh, ksh, and anything we do not know.
[ -f "$CONFIG_DIRECTORY"/profile ] && . "$CONFIG_DIRECTORY"/profile
[ -f "$HOME"/.profile ] && . "$HOME"/.profile
;;
esac
if [ "$XDG_SESSION_TYPE" = "x11" ]; then
[ -f "$CONFIG_DIRECTORY"/xprofile ] && . "$CONFIG_DIRECTORY"/xprofile
[ -f "$HOME"/.xprofile ] && . "$HOME"/.xprofile
# run all system xinitrc shell scripts.
if [ -d "$CONFIG_DIRECTORY"/X11/xinit/xinitrc.d ]; then
for i in "$CONFIG_DIRECTORY"/X11/xinit/xinitrc.d/* ; do
if [ -x "$i" ]; then
. "$i"
fi
done
fi
# Load Xsession scripts
# OPTIONFILE, USERXSESSION, USERXSESSIONRC and ALTUSERXSESSION are required
# by the scripts to work
xsessionddir="$CONFIG_DIRECTORY"/X11/Xsession.d
export OPTIONFILE="$CONFIG_DIRECTORY"/X11/Xsession.options
export USERXSESSION="$HOME"/.xsession
export USERXSESSIONRC="$HOME"/.xsessionrc
export ALTUSERXSESSION="$HOME"/.Xsession
if [ -d "$xsessionddir" ]; then
for i in $(ls "$xsessionddir"); do
script="$xsessionddir/$i"
echo "Loading X session script $script"
if [ -r "$script" ] && [ -f "$script" ] && expr "$i" : '^[[:alnum:]_-]\+$' > /dev/null; then
. "$script"
fi
done
fi
if [ -f "$USERXSESSION" ]; then
. "$USERXSESSION"
fi
if [ -d "$CONFIG_DIRECTORY"/X11/Xresources ]; then
for i in "$CONFIG_DIRECTORY"/X11/Xresources/*; do
[ -f "$i" ] && xrdb -merge "$i"
done
elif [ -f "$CONFIG_DIRECTORY"/X11/Xresources ]; then
xrdb -merge "$CONFIG_DIRECTORY"/X11/Xresources
fi
[ -f "$HOME"/.Xresources ] && xrdb -merge "$HOME"/.Xresources
[ -f "$XDG_CONFIG_HOME"/X11/Xresources ] && xrdb -merge "$XDG_CONFIG_HOME"/X11/Xresources
fi
exec "$@"

31
res/valgrind.supp Normal file
View File

@@ -0,0 +1,31 @@
{
pam
Memcheck:Leak
...
obj:/usr/lib/libpam.so.0.84.2
...
}
{
termbox
Memcheck:Leak
...
fun:tb_init
...
}
{
libc/dynamic
Memcheck:Leak
...
fun:_dl_catch_exception
...
}
{
libc/groups
Memcheck:Leak
...
fun:initgroups
...
}

55
res/wsetup.sh Executable file
View File

@@ -0,0 +1,55 @@
#!/bin/sh
# wayland-session - run as user
# Copyright (C) 2015-2016 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
# This file is extracted from kde-workspace (kdm/kfrontend/genkdmconf.c)
# Copyright (C) 2001-2005 Oswald Buddenhagen <ossi@kde.org>
# Note that the respective logout scripts are not sourced.
case $SHELL in
*/bash)
[ -z "$BASH" ] && exec $SHELL $0 "$@"
set +o posix
[ -f /etc/profile ] && . /etc/profile
if [ -f $HOME/.bash_profile ]; then
. $HOME/.bash_profile
elif [ -f $HOME/.bash_login ]; then
. $HOME/.bash_login
elif [ -f $HOME/.profile ]; then
. $HOME/.profile
fi
;;
*/zsh)
[ -z "$ZSH_NAME" ] && exec $SHELL $0 "$@"
[ -d /etc/zsh ] && zdir=/etc/zsh || zdir=/etc
zhome=${ZDOTDIR:-$HOME}
# zshenv is always sourced automatically.
[ -f $zdir/zprofile ] && . $zdir/zprofile
[ -f $zhome/.zprofile ] && . $zhome/.zprofile
[ -f $zdir/zlogin ] && . $zdir/zlogin
[ -f $zhome/.zlogin ] && . $zhome/.zlogin
emulate -R sh
;;
*/csh|*/tcsh)
# [t]cshrc is always sourced automatically.
# Note that sourcing csh.login after .cshrc is non-standard.
wlsess_tmp=`mktemp /tmp/wlsess-env-XXXXXX`
$SHELL -c "if (-f /etc/csh.login) source /etc/csh.login; if (-f ~/.login) source ~/.login; /bin/sh -c 'export -p' >! $wlsess_tmp"
. $wlsess_tmp
rm -f $wlsess_tmp
;;
*/fish)
[ -f /etc/profile ] && . /etc/profile
[ -f $HOME/.profile ] && . $HOME/.profile
xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX`
$SHELL --login -c "/bin/sh -c 'export -p' > $xsess_tmp"
. $xsess_tmp
rm -f $xsess_tmp
;;
*) # Plain sh, ksh, and anything we do not know.
[ -f /etc/profile ] && . /etc/profile
[ -f $HOME/.profile ] && . $HOME/.profile
;;
esac
exec "$@"

104
res/xsetup.sh Executable file
View File

@@ -0,0 +1,104 @@
#!/bin/sh
# Xsession - run as user
# Copyright (C) 2016 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
# This file is extracted from kde-workspace (kdm/kfrontend/genkdmconf.c)
# Copyright (C) 2001-2005 Oswald Buddenhagen <ossi@kde.org>
# Note that the respective logout scripts are not sourced.
case $SHELL in
*/bash)
[ -z "$BASH" ] && exec $SHELL $0 "$@"
set +o posix
[ -f /etc/profile ] && . /etc/profile
if [ -f $HOME/.bash_profile ]; then
. $HOME/.bash_profile
elif [ -f $HOME/.bash_login ]; then
. $HOME/.bash_login
elif [ -f $HOME/.profile ]; then
. $HOME/.profile
fi
;;
*/zsh)
[ -z "$ZSH_NAME" ] && exec $SHELL $0 "$@"
[ -d /etc/zsh ] && zdir=/etc/zsh || zdir=/etc
zhome=${ZDOTDIR:-$HOME}
# zshenv is always sourced automatically.
[ -f $zdir/zprofile ] && . $zdir/zprofile
[ -f $zhome/.zprofile ] && . $zhome/.zprofile
[ -f $zdir/zlogin ] && . $zdir/zlogin
[ -f $zhome/.zlogin ] && . $zhome/.zlogin
emulate -R sh
;;
*/csh|*/tcsh)
# [t]cshrc is always sourced automatically.
# Note that sourcing csh.login after .cshrc is non-standard.
xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX`
$SHELL -c "if (-f /etc/csh.login) source /etc/csh.login; if (-f ~/.login) source ~/.login; /bin/sh -c 'export -p' >! $xsess_tmp"
. $xsess_tmp
rm -f $xsess_tmp
;;
*/fish)
[ -f /etc/profile ] && . /etc/profile
[ -f $HOME/.profile ] && . $HOME/.profile
xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX`
$SHELL --login -c "/bin/sh -c 'export -p' > $xsess_tmp"
. $xsess_tmp
rm -f $xsess_tmp
;;
*) # Plain sh, ksh, and anything we do not know.
[ -f /etc/profile ] && . /etc/profile
[ -f $HOME/.profile ] && . $HOME/.profile
;;
esac
[ -f /etc/xprofile ] && . /etc/xprofile
[ -f $HOME/.xprofile ] && . $HOME/.xprofile
# run all system xinitrc shell scripts.
if [ -d /etc/X11/xinit/xinitrc.d ]; then
for i in /etc/X11/xinit/xinitrc.d/* ; do
if [ -x "$i" ]; then
. "$i"
fi
done
fi
# Load Xsession scripts
# OPTIONFILE, USERXSESSION, USERXSESSIONRC and ALTUSERXSESSION are required
# by the scripts to work
xsessionddir="/etc/X11/Xsession.d"
OPTIONFILE=/etc/X11/Xsession.options
USERXSESSION=$HOME/.xsession
USERXSESSIONRC=$HOME/.xsessionrc
ALTUSERXSESSION=$HOME/.Xsession
if [ -d "$xsessionddir" ]; then
for i in `ls $xsessionddir`; do
script="$xsessionddir/$i"
echo "Loading X session script $script"
if [ -r "$script" -a -f "$script" ] && expr "$i" : '^[[:alnum:]_-]\+$' > /dev/null; then
. "$script"
fi
done
fi
if [ -f "$USERXSESSION" ]; then
. "$USERXSESSION"
fi
if [ -d /etc/X11/Xresources ]; then
for i in /etc/X11/Xresources/*; do
[ -f $i ] && xrdb -merge $i
done
elif [ -f /etc/X11/Xresources ]; then
xrdb -merge /etc/X11/Xresources
fi
[ -f $HOME/.Xresources ] && xrdb -merge $HOME/.Xresources
[ -f $XDG_CONFIG_HOME/X11/Xresources ] && xrdb -merge $XDG_CONFIG_HOME/X11/Xresources
if [ -z "$*" ]; then
exec xmessage -center -buttons OK:0 -default OK "Sorry, $DESKTOP_SESSION is no valid session."
else
exec $@
fi

View File

@@ -1,21 +0,0 @@
const enums = @import("enums.zig");
const ini = @import("zigini");
const DisplayServer = enums.DisplayServer;
const Ini = ini.Ini;
pub const DesktopEntry = struct {
Exec: []const u8 = "",
Name: [:0]const u8 = "",
DesktopNames: ?[:0]u8 = null,
};
pub const Entry = struct { @"Desktop Entry": DesktopEntry = .{} };
entry_ini: ?Ini(Entry) = null,
name: [:0]const u8 = "",
xdg_session_desktop: ?[:0]const u8 = null,
xdg_desktop_names: ?[:0]const u8 = null,
cmd: []const u8 = "",
specifier: []const u8 = "",
display_server: DisplayServer = .wayland,

View File

@@ -9,7 +9,7 @@ const ErrorHandler = packed struct {
const SharedError = @This(); const SharedError = @This();
data: []align(std.heap.page_size_min) u8, data: []align(std.mem.page_size) u8,
pub fn init() !SharedError { pub fn init() !SharedError {
const data = try std.posix.mmap(null, @sizeOf(ErrorHandler), std.posix.PROT.READ | std.posix.PROT.WRITE, .{ .TYPE = .SHARED, .ANONYMOUS = true }, -1, 0); const data = try std.posix.mmap(null, @sizeOf(ErrorHandler), std.posix.PROT.READ | std.posix.PROT.WRITE, .{ .TYPE = .SHARED, .ANONYMOUS = true }, -1, 0);

View File

@@ -1,86 +0,0 @@
const std = @import("std");
const Animation = @import("../tui/Animation.zig");
const Cell = @import("../tui/Cell.zig");
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
const ColorMix = @This();
const math = std.math;
const Vec2 = @Vector(2, f32);
const time_scale: f32 = 0.01;
const palette_len: usize = 12;
fn length(vec: Vec2) f32 {
return math.sqrt(vec[0] * vec[0] + vec[1] * vec[1]);
}
terminal_buffer: *TerminalBuffer,
frames: u64,
pattern_cos_mod: f32,
pattern_sin_mod: f32,
palette: [palette_len]Cell,
pub fn init(terminal_buffer: *TerminalBuffer, col1: u32, col2: u32, col3: u32) ColorMix {
return .{
.terminal_buffer = terminal_buffer,
.frames = 0,
.pattern_cos_mod = terminal_buffer.random.float(f32) * math.pi * 2.0,
.pattern_sin_mod = terminal_buffer.random.float(f32) * math.pi * 2.0,
.palette = [palette_len]Cell{
Cell.init(0x2588, col1, col2),
Cell.init(0x2593, col1, col2),
Cell.init(0x2592, col1, col2),
Cell.init(0x2591, col1, col2),
Cell.init(0x2588, col2, col3),
Cell.init(0x2593, col2, col3),
Cell.init(0x2592, col2, col3),
Cell.init(0x2591, col2, col3),
Cell.init(0x2588, col3, col1),
Cell.init(0x2593, col3, col1),
Cell.init(0x2592, col3, col1),
Cell.init(0x2591, col3, col1),
},
};
}
pub fn animation(self: *ColorMix) Animation {
return Animation.init(self, deinit, realloc, draw);
}
fn deinit(_: *ColorMix) void {}
fn realloc(_: *ColorMix) anyerror!void {}
fn draw(self: *ColorMix) void {
self.frames +%= 1;
const time: f32 = @as(f32, @floatFromInt(self.frames)) * time_scale;
for (0..self.terminal_buffer.width) |x| {
for (0..self.terminal_buffer.height) |y| {
const xi: i32 = @intCast(x);
const yi: i32 = @intCast(y);
const wi: i32 = @intCast(self.terminal_buffer.width);
const hi: i32 = @intCast(self.terminal_buffer.height);
var uv: Vec2 = .{
@as(f32, @floatFromInt(xi * 2 - wi)) / @as(f32, @floatFromInt(self.terminal_buffer.height * 2)),
@as(f32, @floatFromInt(yi * 2 - hi)) / @as(f32, @floatFromInt(self.terminal_buffer.height)),
};
var uv2: Vec2 = @splat(uv[0] + uv[1]);
for (0..3) |_| {
uv2 += uv + @as(Vec2, @splat(length(uv)));
uv += @as(Vec2, @splat(0.5)) * Vec2{
math.cos(self.pattern_cos_mod + uv2[1] * 0.2 + time * 0.1),
math.sin(self.pattern_sin_mod + uv2[0] - time * 0.1),
};
uv -= @splat(1.0 * math.cos(uv[0] + uv[1]) - math.sin(uv[0] * 0.7 - uv[1]));
}
const cell = self.palette[@as(usize, @intFromFloat(math.floor(length(uv) * 5.0))) % palette_len];
cell.put(x, y);
}
}
}

View File

@@ -1,19 +1,35 @@
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Animation = @import("../tui/Animation.zig");
const Cell = @import("../tui/Cell.zig");
const TerminalBuffer = @import("../tui/TerminalBuffer.zig"); const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
const utils = @import("../tui/utils.zig");
const interop = @import("../interop.zig");
const termbox = interop.termbox;
const Doom = @This(); const Doom = @This();
pub const STEPS = 12; pub const STEPS = 13;
pub const FIRE = [_]termbox.tb_cell{
utils.initCell(' ', 9, 0),
utils.initCell(0x2591, 2, 0), // Red
utils.initCell(0x2592, 2, 0), // Red
utils.initCell(0x2593, 2, 0), // Red
utils.initCell(0x2588, 2, 0), // Red
utils.initCell(0x2591, 4, 2), // Yellow
utils.initCell(0x2592, 4, 2), // Yellow
utils.initCell(0x2593, 4, 2), // Yellow
utils.initCell(0x2588, 4, 2), // Yellow
utils.initCell(0x2591, 8, 4), // White
utils.initCell(0x2592, 8, 4), // White
utils.initCell(0x2593, 8, 4), // White
utils.initCell(0x2588, 8, 4), // White
};
allocator: Allocator, allocator: Allocator,
terminal_buffer: *TerminalBuffer, terminal_buffer: *TerminalBuffer,
buffer: []u8, buffer: []u8,
fire: [STEPS + 1]Cell,
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, top_color: u32, middle_color: u32, bottom_color: u32) !Doom { pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer) !Doom {
const buffer = try allocator.alloc(u8, terminal_buffer.width * terminal_buffer.height); const buffer = try allocator.alloc(u8, terminal_buffer.width * terminal_buffer.height);
initBuffer(buffer, terminal_buffer.width); initBuffer(buffer, terminal_buffer.width);
@@ -21,66 +37,48 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, top_color: u
.allocator = allocator, .allocator = allocator,
.terminal_buffer = terminal_buffer, .terminal_buffer = terminal_buffer,
.buffer = buffer, .buffer = buffer,
.fire = [_]Cell{
Cell.init(' ', TerminalBuffer.Color.DEFAULT, TerminalBuffer.Color.DEFAULT),
Cell.init(0x2591, top_color, TerminalBuffer.Color.DEFAULT),
Cell.init(0x2592, top_color, TerminalBuffer.Color.DEFAULT),
Cell.init(0x2593, top_color, TerminalBuffer.Color.DEFAULT),
Cell.init(0x2588, top_color, TerminalBuffer.Color.DEFAULT),
Cell.init(0x2591, middle_color, top_color),
Cell.init(0x2592, middle_color, top_color),
Cell.init(0x2593, middle_color, top_color),
Cell.init(0x2588, middle_color, top_color),
Cell.init(0x2591, bottom_color, middle_color),
Cell.init(0x2592, bottom_color, middle_color),
Cell.init(0x2593, bottom_color, middle_color),
Cell.init(0x2588, bottom_color, middle_color),
},
}; };
} }
pub fn animation(self: *Doom) Animation { pub fn deinit(self: Doom) void {
return Animation.init(self, deinit, realloc, draw);
}
fn deinit(self: *Doom) void {
self.allocator.free(self.buffer); self.allocator.free(self.buffer);
} }
fn realloc(self: *Doom) anyerror!void { pub fn realloc(self: *Doom) !void {
const buffer = try self.allocator.realloc(self.buffer, self.terminal_buffer.width * self.terminal_buffer.height); const buffer = try self.allocator.realloc(self.buffer, self.terminal_buffer.width * self.terminal_buffer.height);
initBuffer(buffer, self.terminal_buffer.width); initBuffer(buffer, self.terminal_buffer.width);
self.buffer = buffer; self.buffer = buffer;
} }
fn draw(self: *Doom) void { pub fn draw(self: Doom) void {
for (0..self.terminal_buffer.width) |x| { for (0..self.terminal_buffer.width) |x| {
// We start from 1 so that we always have the topmost line when spreading fire
for (1..self.terminal_buffer.height) |y| { for (1..self.terminal_buffer.height) |y| {
// Get current cell const source = y * self.terminal_buffer.width + x;
const from = y * self.terminal_buffer.width + x; const random = (self.terminal_buffer.random.int(u16) % 7) & 3;
const cell_index = self.buffer[from];
// Spread fire var dest = (source - @min(source, random)) + 1;
const propagate = self.terminal_buffer.random.int(u1); if (self.terminal_buffer.width > dest) dest = 0 else dest -= self.terminal_buffer.width;
const to = from - self.terminal_buffer.width; // Get the line above
self.buffer[to] = if (cell_index > 0) cell_index - propagate else cell_index; const buffer_source = self.buffer[source];
const buffer_dest_offset = random & 1;
// Put the cell if (buffer_source < buffer_dest_offset) continue;
const cell = self.fire[cell_index];
cell.put(x, y); var buffer_dest = buffer_source - buffer_dest_offset;
if (buffer_dest > 12) buffer_dest = 0;
self.buffer[dest] = @intCast(buffer_dest);
self.terminal_buffer.buffer[dest] = FIRE[buffer_dest];
self.terminal_buffer.buffer[source] = FIRE[buffer_source];
} }
} }
} }
fn initBuffer(buffer: []u8, width: usize) void { fn initBuffer(buffer: []u8, width: u64) void {
const length = buffer.len - width; const length = buffer.len - width;
const slice_start = buffer[0..length]; const slice_start = buffer[0..length];
const slice_end = buffer[length..]; const slice_end = buffer[length..];
// Initialize the framebuffer in black, except for the "fire source" as the
// last color
@memset(slice_start, 0); @memset(slice_start, 0);
@memset(slice_end, STEPS); @memset(slice_end, STEPS - 1);
} }

View File

@@ -1,14 +0,0 @@
const std = @import("std");
const Animation = @import("../tui/Animation.zig");
const Dummy = @This();
pub fn animation(self: *Dummy) Animation {
return Animation.init(self, deinit, realloc, draw);
}
fn deinit(_: *Dummy) void {}
fn realloc(_: *Dummy) anyerror!void {}
fn draw(_: *Dummy) void {}

View File

@@ -1,43 +1,41 @@
const std = @import("std"); const std = @import("std");
const Animation = @import("../tui/Animation.zig"); const Allocator = std.mem.Allocator;
const Cell = @import("../tui/Cell.zig"); const Random = std.rand.Random;
const TerminalBuffer = @import("../tui/TerminalBuffer.zig"); const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
const Allocator = std.mem.Allocator; const interop = @import("../interop.zig");
const Random = std.Random; const termbox = interop.termbox;
pub const FRAME_DELAY: usize = 8; pub const FRAME_DELAY: u64 = 8;
// Allowed codepoints
pub const MIN_CODEPOINT: isize = 33;
pub const MAX_CODEPOINT: isize = 123 - MIN_CODEPOINT;
// Characters change mid-scroll // Characters change mid-scroll
pub const MID_SCROLL_CHANGE = true; pub const MID_SCROLL_CHANGE = true;
const DOT_HEAD_COLOR: u32 = @intCast(TerminalBuffer.Color.WHITE | TerminalBuffer.Styling.BOLD);
const Matrix = @This(); const Matrix = @This();
pub const Dot = struct { pub const Dot = struct {
value: ?usize, value: isize,
is_head: bool, is_head: bool,
}; };
pub const Line = struct { pub const Line = struct {
space: usize, space: isize,
length: usize, length: isize,
update: usize, update: isize,
}; };
allocator: Allocator, allocator: Allocator,
terminal_buffer: *TerminalBuffer, terminal_buffer: *TerminalBuffer,
dots: []Dot, dots: []Dot,
lines: []Line, lines: []Line,
frame: usize, frame: u64,
count: usize, count: u64,
fg: u32,
min_codepoint: u16,
max_codepoint: u16,
default_cell: Cell,
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg: u32, min_codepoint: u16, max_codepoint: u16) !Matrix { pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer) !Matrix {
const dots = try allocator.alloc(Dot, terminal_buffer.width * (terminal_buffer.height + 1)); const dots = try allocator.alloc(Dot, terminal_buffer.width * (terminal_buffer.height + 1));
const lines = try allocator.alloc(Line, terminal_buffer.width); const lines = try allocator.alloc(Line, terminal_buffer.width);
@@ -50,23 +48,15 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg: u32, min
.lines = lines, .lines = lines,
.frame = 3, .frame = 3,
.count = 0, .count = 0,
.fg = fg,
.min_codepoint = min_codepoint,
.max_codepoint = max_codepoint - min_codepoint,
.default_cell = .{ .ch = ' ', .fg = fg, .bg = terminal_buffer.bg },
}; };
} }
pub fn animation(self: *Matrix) Animation { pub fn deinit(self: Matrix) void {
return Animation.init(self, deinit, realloc, draw);
}
fn deinit(self: *Matrix) void {
self.allocator.free(self.dots); self.allocator.free(self.dots);
self.allocator.free(self.lines); self.allocator.free(self.lines);
} }
fn realloc(self: *Matrix) anyerror!void { pub fn realloc(self: *Matrix) !void {
const dots = try self.allocator.realloc(self.dots, self.terminal_buffer.width * (self.terminal_buffer.height + 1)); const dots = try self.allocator.realloc(self.dots, self.terminal_buffer.width * (self.terminal_buffer.height + 1));
const lines = try self.allocator.realloc(self.lines, self.terminal_buffer.width); const lines = try self.allocator.realloc(self.lines, self.terminal_buffer.width);
@@ -76,7 +66,7 @@ fn realloc(self: *Matrix) anyerror!void {
self.lines = lines; self.lines = lines;
} }
fn draw(self: *Matrix) void { pub fn draw(self: *Matrix) void {
const buf_height = self.terminal_buffer.height; const buf_height = self.terminal_buffer.height;
const buf_width = self.terminal_buffer.width; const buf_width = self.terminal_buffer.width;
self.count += 1; self.count += 1;
@@ -85,31 +75,31 @@ fn draw(self: *Matrix) void {
if (self.frame > 4) self.frame = 1; if (self.frame > 4) self.frame = 1;
self.count = 0; self.count = 0;
var x: usize = 0; var x: u64 = 0;
while (x < self.terminal_buffer.width) : (x += 2) { while (x < self.terminal_buffer.width) : (x += 2) {
var tail: usize = 0; var tail: u64 = 0;
var line = &self.lines[x]; var line = &self.lines[x];
if (self.frame <= line.update) continue; if (self.frame <= line.update) continue;
if (self.dots[x].value == null and self.dots[self.terminal_buffer.width + x].value == ' ') { if (self.dots[x].value == -1 and self.dots[self.terminal_buffer.width + x].value == ' ') {
if (line.space > 0) { if (line.space > 0) {
line.space -= 1; line.space -= 1;
} else { } else {
const randint = self.terminal_buffer.random.int(u16); const randint = self.terminal_buffer.random.int(i16);
const h = self.terminal_buffer.height; const h: isize = @intCast(self.terminal_buffer.height);
line.length = @mod(randint, h - 3) + 3; line.length = @mod(randint, h - 3) + 3;
self.dots[x].value = @mod(randint, self.max_codepoint) + self.min_codepoint; self.dots[x].value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT;
line.space = @mod(randint, h + 1); line.space = @mod(randint, h + 1);
} }
} }
var y: usize = 0; var y: u64 = 0;
var first_col = true; var first_col = true;
var seg_len: u64 = 0; var seg_len: u64 = 0;
height_it: while (y <= buf_height) : (y += 1) { height_it: while (y <= buf_height) : (y += 1) {
var dot = &self.dots[buf_width * y + x]; var dot = &self.dots[buf_width * y + x];
// Skip over spaces // Skip over spaces
while (y <= buf_height and (dot.value == ' ' or dot.value == null)) { while (y <= buf_height and (dot.value == ' ' or dot.value == -1)) {
y += 1; y += 1;
if (y > buf_height) break :height_it; if (y > buf_height) break :height_it;
dot = &self.dots[buf_width * y + x]; dot = &self.dots[buf_width * y + x];
@@ -118,12 +108,12 @@ fn draw(self: *Matrix) void {
// Find the head of this column // Find the head of this column
tail = y; tail = y;
seg_len = 0; seg_len = 0;
while (y <= buf_height and dot.value != ' ' and dot.value != null) { while (y <= buf_height and dot.value != ' ' and dot.value != -1) {
dot.is_head = false; dot.is_head = false;
if (MID_SCROLL_CHANGE) { if (MID_SCROLL_CHANGE) {
const randint = self.terminal_buffer.random.int(u16); const randint = self.terminal_buffer.random.int(i16);
if (@mod(randint, 8) == 0) { if (@mod(randint, 8) == 0) {
dot.value = @mod(randint, self.max_codepoint) + self.min_codepoint; dot.value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT;
} }
} }
@@ -137,50 +127,53 @@ fn draw(self: *Matrix) void {
dot = &self.dots[buf_width * y + x]; dot = &self.dots[buf_width * y + x];
} }
const randint = self.terminal_buffer.random.int(u16); const randint = self.terminal_buffer.random.int(i16);
dot.value = @mod(randint, self.max_codepoint) + self.min_codepoint; dot.value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT;
dot.is_head = true; dot.is_head = true;
if (seg_len > line.length or !first_col) { if (seg_len > line.length or !first_col) {
self.dots[buf_width * tail + x].value = ' '; self.dots[buf_width * tail + x].value = ' ';
self.dots[x].value = null; self.dots[x].value = -1;
} }
first_col = false; first_col = false;
} }
} }
} }
var x: usize = 0; var x: u64 = 0;
while (x < buf_width) : (x += 2) { while (x < buf_width) : (x += 2) {
var y: usize = 1; var y: u64 = 1;
while (y <= self.terminal_buffer.height) : (y += 1) { while (y <= self.terminal_buffer.height) : (y += 1) {
const dot = self.dots[buf_width * y + x]; const dot = self.dots[buf_width * y + x];
const cell = if (dot.value == null or dot.value == ' ') self.default_cell else Cell{ var fg: u16 = @intCast(termbox.TB_GREEN);
.ch = @intCast(dot.value.?),
.fg = if (dot.is_head) DOT_HEAD_COLOR else self.fg,
.bg = self.terminal_buffer.bg,
};
cell.put(x, y - 1); if (dot.value == -1 or dot.value == ' ') {
_ = termbox.tb_set_cell(@intCast(x), @intCast(y - 1), ' ', fg, termbox.TB_DEFAULT);
continue;
}
if (dot.is_head) fg = @intCast(termbox.TB_WHITE | termbox.TB_BOLD);
_ = termbox.tb_set_cell(@intCast(x), @intCast(y - 1), @intCast(dot.value), fg, termbox.TB_DEFAULT);
} }
} }
} }
fn initBuffers(dots: []Dot, lines: []Line, width: usize, height: usize, random: Random) void { fn initBuffers(dots: []Dot, lines: []Line, width: u64, height: u64, random: Random) void {
var y: usize = 0; var y: u64 = 0;
while (y <= height) : (y += 1) { while (y <= height) : (y += 1) {
var x: usize = 0; var x: u64 = 0;
while (x < width) : (x += 2) { while (x < width) : (x += 2) {
dots[y * width + x].value = null; dots[y * width + x].value = -1;
} }
} }
var x: usize = 0; var x: u64 = 0;
while (x < width) : (x += 2) { while (x < width) : (x += 2) {
var line = lines[x]; var line = lines[x];
line.space = @mod(random.int(u16), height) + 1; const h: isize = @intCast(height);
line.length = @mod(random.int(u16), height - 3) + 3; line.space = @mod(random.int(i16), h) + 1;
line.update = @mod(random.int(u16), 3) + 1; line.length = @mod(random.int(i16), h - 3) + 3;
line.update = @mod(random.int(i16), 3) + 1;
lines[x] = line; lines[x] = line;
dots[width + x].value = ' '; dots[width + x].value = ' ';

View File

@@ -1,27 +1,14 @@
const std = @import("std"); const std = @import("std");
const build_options = @import("build_options");
const builtin = @import("builtin");
const enums = @import("enums.zig"); const enums = @import("enums.zig");
const Environment = @import("Environment.zig");
const interop = @import("interop.zig"); const interop = @import("interop.zig");
const SharedError = @import("SharedError.zig"); const TerminalBuffer = @import("tui/TerminalBuffer.zig");
const Desktop = @import("tui/components/Desktop.zig");
const Text = @import("tui/components/Text.zig");
const Config = @import("config/Config.zig");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Md5 = std.crypto.hash.Md5;
const utmp = interop.utmp; const utmp = interop.utmp;
const Utmp = utmp.utmpx; const Utmp = utmp.utmp;
const SharedError = @import("SharedError.zig");
pub const AuthOptions = struct {
tty: u8,
service_name: [:0]const u8,
path: ?[:0]const u8,
session_log: []const u8,
xauth_cmd: []const u8,
setup_cmd: []const u8,
login_cmd: ?[]const u8,
x_cmd: []const u8,
session_pid: std.posix.pid_t,
};
var xorg_pid: std.posix.pid_t = 0; var xorg_pid: std.posix.pid_t = 0;
pub fn xorgSignalHandler(i: c_int) callconv(.C) void { pub fn xorgSignalHandler(i: c_int) callconv(.C) void {
@@ -33,16 +20,16 @@ pub fn sessionSignalHandler(i: c_int) callconv(.C) void {
if (child_pid > 0) _ = std.c.kill(child_pid, i); if (child_pid > 0) _ = std.c.kill(child_pid, i);
} }
pub fn authenticate(options: AuthOptions, current_environment: Environment, login: [:0]const u8, password: [:0]const u8) !void { pub fn authenticate(config: Config, current_environment: Desktop.Environment, login: [:0]const u8, password: [:0]const u8) !void {
var tty_buffer: [3]u8 = undefined; var tty_buffer: [3]u8 = undefined;
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{options.tty}); const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty});
var pam_tty_buffer: [6]u8 = undefined; var pam_tty_buffer: [6]u8 = undefined;
const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{options.tty}); const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{config.tty});
// Set the XDG environment variables // Set the XDG environment variables
setXdgSessionEnv(current_environment.display_server); setXdgSessionEnv(current_environment.display_server);
try setXdgEnv(tty_str, current_environment.xdg_session_desktop, current_environment.xdg_desktop_names); try setXdgEnv(tty_str, current_environment.xdg_session_desktop, current_environment.xdg_desktop_names orelse "");
// Open the PAM session // Open the PAM session
var credentials = [_:null]?[*:0]const u8{ login, password }; var credentials = [_:null]?[*:0]const u8{ login, password };
@@ -53,7 +40,7 @@ pub fn authenticate(options: AuthOptions, current_environment: Environment, logi
}; };
var handle: ?*interop.pam.pam_handle = undefined; var handle: ?*interop.pam.pam_handle = undefined;
var status = interop.pam.pam_start(options.service_name, null, &conv, &handle); var status = interop.pam.pam_start(config.service_name.ptr, null, &conv, &handle);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
defer _ = interop.pam.pam_end(handle, status); defer _ = interop.pam.pam_end(handle, status);
@@ -76,19 +63,19 @@ pub fn authenticate(options: AuthOptions, current_environment: Environment, logi
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
defer status = interop.pam.pam_close_session(handle, 0); defer status = interop.pam.pam_close_session(handle, 0);
var pwd: *interop.pwd.passwd = undefined; var pwd: *interop.passwd = undefined;
{ {
defer interop.pwd.endpwent(); defer interop.endpwent();
// Get password structure from username // Get password structure from username
pwd = interop.pwd.getpwnam(login) orelse return error.GetPasswordNameFailed; pwd = interop.getpwnam(login.ptr) orelse return error.GetPasswordNameFailed;
} }
// Set user shell if it hasn't already been set // Set user shell if it hasn't already been set
if (pwd.pw_shell == null) { if (pwd.pw_shell[0] == 0) {
interop.unistd.setusershell(); interop.setusershell();
pwd.pw_shell = interop.unistd.getusershell(); pwd.pw_shell = interop.getusershell();
interop.unistd.endusershell(); interop.endusershell();
} }
var shared_err = try SharedError.init(); var shared_err = try SharedError.init();
@@ -96,7 +83,7 @@ pub fn authenticate(options: AuthOptions, current_environment: Environment, logi
child_pid = try std.posix.fork(); child_pid = try std.posix.fork();
if (child_pid == 0) { if (child_pid == 0) {
startSession(options, tty_str, pwd, handle, current_environment) catch |e| { startSession(config, pwd, handle, current_environment) catch |e| {
shared_err.writeError(e); shared_err.writeError(e);
std.process.exit(1); std.process.exit(1);
}; };
@@ -118,113 +105,91 @@ pub fn authenticate(options: AuthOptions, current_environment: Environment, logi
.mask = std.posix.empty_sigset, .mask = std.posix.empty_sigset,
.flags = 0, .flags = 0,
}; };
std.posix.sigaction(std.posix.SIG.TERM, &act, null); try std.posix.sigaction(std.posix.SIG.TERM, &act, null);
try addUtmpEntry(&entry, pwd.pw_name.?, child_pid); try addUtmpEntry(&entry, pwd.pw_name, child_pid);
} }
// Wait for the session to stop // Wait for the session to stop
_ = std.posix.waitpid(child_pid, 0); _ = std.posix.waitpid(child_pid, 0);
removeUtmpEntry(&entry); removeUtmpEntry(&entry);
try resetTerminal(pwd.pw_shell, config.term_reset_cmd);
if (shared_err.readError()) |err| return err; if (shared_err.readError()) |err| return err;
} }
fn startSession( fn startSession(
options: AuthOptions, config: Config,
tty_str: [:0]u8, pwd: *interop.passwd,
pwd: *interop.pwd.passwd,
handle: ?*interop.pam.pam_handle, handle: ?*interop.pam.pam_handle,
current_environment: Environment, current_environment: Desktop.Environment,
) !void { ) !void {
if (builtin.os.tag == .freebsd) { const status = interop.initgroups(pwd.pw_name, pwd.pw_gid);
// FreeBSD has initgroups() in unistd if (status != 0) return error.GroupInitializationFailed;
const status = interop.unistd.initgroups(pwd.pw_name, pwd.pw_gid);
if (status != 0) return error.GroupInitializationFailed;
// FreeBSD sets the GID and UID with setusercontext() std.posix.setgid(pwd.pw_gid) catch return error.SetUserGidFailed;
const result = interop.pwd.setusercontext(null, pwd, pwd.pw_uid, interop.pwd.LOGIN_SETALL); std.posix.setuid(pwd.pw_uid) catch return error.SetUserUidFailed;
if (result != 0) return error.SetUserUidFailed;
} else {
const status = interop.grp.initgroups(pwd.pw_name, pwd.pw_gid);
if (status != 0) return error.GroupInitializationFailed;
std.posix.setgid(pwd.pw_gid) catch return error.SetUserGidFailed;
std.posix.setuid(pwd.pw_uid) catch return error.SetUserUidFailed;
}
// Set up the environment // Set up the environment
try initEnv(pwd, options.path); try initEnv(pwd, config.path);
// Reset the XDG environment variables
setXdgSessionEnv(current_environment.display_server);
try setXdgEnv(tty_str, current_environment.xdg_session_desktop, current_environment.xdg_desktop_names);
// Set the PAM variables // Set the PAM variables
const pam_env_vars: ?[*:null]?[*:0]u8 = interop.pam.pam_getenvlist(handle); const pam_env_vars: ?[*:null]?[*:0]u8 = interop.pam.pam_getenvlist(handle);
if (pam_env_vars == null) return error.GetEnvListFailed; if (pam_env_vars == null) return error.GetEnvListFailed;
const env_list = std.mem.span(pam_env_vars.?); const env_list = std.mem.span(pam_env_vars.?);
for (env_list) |env_var| _ = interop.stdlib.putenv(env_var); for (env_list) |env_var| _ = interop.putenv(env_var.?);
// Change to the user's home directory
std.posix.chdirZ(pwd.pw_dir.?) catch return error.ChangeDirectoryFailed;
// Signal to the session process to give up control on the TTY
_ = std.posix.kill(options.session_pid, std.posix.SIG.CHLD) catch return error.TtyControlTransferFailed;
// Execute what the user requested // Execute what the user requested
std.posix.chdirZ(pwd.pw_dir) catch return error.ChangeDirectoryFailed;
try resetTerminal(pwd.pw_shell, config.term_reset_cmd);
switch (current_environment.display_server) { switch (current_environment.display_server) {
.wayland => try executeWaylandCmd(pwd.pw_shell.?, options, current_environment.cmd), .wayland => try executeWaylandCmd(pwd.pw_shell, config.wayland_cmd, current_environment.cmd),
.shell => try executeShellCmd(pwd.pw_shell.?, options), .shell => try executeShellCmd(pwd.pw_shell),
.xinitrc, .x11 => if (build_options.enable_x11_support) { .xinitrc, .x11 => {
var vt_buf: [5]u8 = undefined; var vt_buf: [5]u8 = undefined;
const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{options.tty}); const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{config.tty});
try executeX11Cmd(pwd.pw_shell.?, pwd.pw_dir.?, options, current_environment.cmd, vt); try executeX11Cmd(pwd.pw_shell, pwd.pw_dir, config, current_environment.cmd, vt);
}, },
} }
} }
fn initEnv(pwd: *interop.pwd.passwd, path_env: ?[:0]const u8) !void { fn initEnv(pwd: *interop.passwd, path_env: ?[:0]const u8) !void {
_ = interop.stdlib.setenv("HOME", pwd.pw_dir, 1); _ = interop.setenv("HOME", pwd.pw_dir, 1);
_ = interop.stdlib.setenv("PWD", pwd.pw_dir, 1); _ = interop.setenv("PWD", pwd.pw_dir, 1);
_ = interop.stdlib.setenv("SHELL", pwd.pw_shell, 1); _ = interop.setenv("SHELL", pwd.pw_shell, 1);
_ = interop.stdlib.setenv("USER", pwd.pw_name, 1); _ = interop.setenv("USER", pwd.pw_name, 1);
_ = interop.stdlib.setenv("LOGNAME", pwd.pw_name, 1); _ = interop.setenv("LOGNAME", pwd.pw_name, 1);
if (path_env) |path| { if (path_env) |path| {
const status = interop.stdlib.setenv("PATH", path, 1); const status = interop.setenv("PATH", path, 1);
if (status != 0) return error.SetPathFailed; if (status != 0) return error.SetPathFailed;
} }
} }
fn setXdgSessionEnv(display_server: enums.DisplayServer) void { fn setXdgSessionEnv(display_server: enums.DisplayServer) void {
_ = interop.stdlib.setenv("XDG_SESSION_TYPE", switch (display_server) { _ = interop.setenv("XDG_SESSION_TYPE", switch (display_server) {
.wayland => "wayland", .wayland => "wayland",
.shell => "tty", .shell => "tty",
.xinitrc, .x11 => "x11", .xinitrc, .x11 => "x11",
}, 0); }, 0);
} }
fn setXdgEnv(tty_str: [:0]u8, maybe_desktop_name: ?[:0]const u8, maybe_xdg_desktop_names: ?[:0]const u8) !void { fn setXdgEnv(tty_str: [:0]u8, desktop_name: [:0]const u8, xdg_desktop_names: [:0]const u8) !void {
// The "/run/user/%d" directory is not available on FreeBSD. It is much const uid = interop.getuid();
// better to stick to the defaults and let applications using var uid_buffer: [10 + @sizeOf(u32) + 1]u8 = undefined;
// XDG_RUNTIME_DIR to fall back to directories inside user's home const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid});
// directory.
if (builtin.os.tag != .freebsd) {
const uid = interop.unistd.getuid();
var uid_buffer: [32]u8 = undefined; // No UID can be larger than this
const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid});
_ = interop.stdlib.setenv("XDG_RUNTIME_DIR", uid_str, 0); if (!std.mem.eql(u8, xdg_desktop_names, "")) _ = interop.setenv("XDG_CURRENT_DESKTOP", xdg_desktop_names.ptr, 0);
} _ = interop.setenv("XDG_RUNTIME_DIR", uid_str.ptr, 0);
_ = interop.setenv("XDG_SESSION_CLASS", "user", 0);
if (maybe_xdg_desktop_names) |xdg_desktop_names| _ = interop.stdlib.setenv("XDG_CURRENT_DESKTOP", xdg_desktop_names, 0); _ = interop.setenv("XDG_SESSION_ID", "1", 0);
_ = interop.stdlib.setenv("XDG_SESSION_CLASS", "user", 0); if (!std.mem.eql(u8, desktop_name, "")) _ = interop.setenv("XDG_SESSION_DESKTOP", desktop_name.ptr, 0);
_ = interop.stdlib.setenv("XDG_SESSION_ID", "1", 0); _ = interop.setenv("XDG_SEAT", "seat0", 0);
if (maybe_desktop_name) |desktop_name| _ = interop.stdlib.setenv("XDG_SESSION_DESKTOP", desktop_name, 0); _ = interop.setenv("XDG_VTNR", tty_str.ptr, 0);
_ = interop.stdlib.setenv("XDG_SEAT", "seat0", 0);
_ = interop.stdlib.setenv("XDG_VTNR", tty_str, 0);
} }
fn loginConv( fn loginConv(
@@ -255,7 +220,7 @@ fn loginConv(
status = interop.pam.PAM_BUF_ERR; status = interop.pam.PAM_BUF_ERR;
break :set_credentials; break :set_credentials;
}; };
response[i].resp = username.?; response[i].resp = username.?.ptr;
}, },
interop.pam.PAM_PROMPT_ECHO_OFF => { interop.pam.PAM_PROMPT_ECHO_OFF => {
const data: [*][*:0]u8 = @ptrCast(@alignCast(appdata_ptr)); const data: [*][*:0]u8 = @ptrCast(@alignCast(appdata_ptr));
@@ -263,7 +228,7 @@ fn loginConv(
status = interop.pam.PAM_BUF_ERR; status = interop.pam.PAM_BUF_ERR;
break :set_credentials; break :set_credentials;
}; };
response[i].resp = password.?; response[i].resp = password.?.ptr;
}, },
interop.pam.PAM_ERROR_MSG => { interop.pam.PAM_ERROR_MSG => {
status = interop.pam.PAM_CONV_ERR; status = interop.pam.PAM_CONV_ERR;
@@ -285,6 +250,17 @@ fn loginConv(
return status; return status;
} }
fn resetTerminal(shell: [*:0]const u8, term_reset_cmd: [:0]const u8) !void {
const pid = try std.posix.fork();
if (pid == 0) {
const args = [_:null]?[*:0]const u8{ shell, "-c", term_reset_cmd };
std.posix.execveZ(shell, &args, std.c.environ) catch {};
std.process.exit(1);
}
_ = std.posix.waitpid(pid, 0);
}
fn getFreeDisplay() !u8 { fn getFreeDisplay() !u8 {
var buf: [15]u8 = undefined; var buf: [15]u8 = undefined;
var i: u8 = 0; var i: u8 = 0;
@@ -358,30 +334,49 @@ fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 {
return xauthority; return xauthority;
} }
fn mcookie() [Md5.digest_length * 2]u8 { pub fn mcookie(shell: [*:0]const u8, cmd: [:0]const u8) ![32]u8 {
var buf: [4096]u8 = undefined; const pipe = try std.posix.pipe();
std.crypto.random.bytes(&buf); defer std.posix.close(pipe[1]);
var out: [Md5.digest_length]u8 = undefined; const output = std.fs.File{ .handle = pipe[0] };
Md5.hash(&buf, &out, .{}); defer output.close();
return std.fmt.bytesToHex(&out, .lower); const pid = try std.posix.fork();
if (pid == 0) {
std.posix.close(pipe[0]);
std.posix.dup2(pipe[1], std.posix.STDOUT_FILENO) catch std.process.exit(1);
std.posix.close(pipe[1]);
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd };
std.posix.execveZ(shell, &args, std.c.environ) catch {};
std.process.exit(1);
}
const result = std.posix.waitpid(pid, 0);
if (result.status != 0) return error.McookieFailed;
var buf: [32]u8 = undefined;
const len = try output.read(&buf);
if (len != 32) return error.McookieFailed;
return buf;
} }
fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptions) !void { fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, xauth_cmd: []const u8, mcookie_cmd: [:0]const u8) !void {
var pwd_buf: [100]u8 = undefined; var pwd_buf: [100]u8 = undefined;
const pwd = try std.fmt.bufPrintZ(&pwd_buf, "{s}", .{pw_dir}); const pwd = try std.fmt.bufPrintZ(&pwd_buf, "{s}", .{pw_dir});
const xauthority = try createXauthFile(pwd); const xauthority = try createXauthFile(pwd);
_ = interop.stdlib.setenv("XAUTHORITY", xauthority, 1); _ = interop.setenv("XAUTHORITY", xauthority, 1);
_ = interop.stdlib.setenv("DISPLAY", display_name, 1); _ = interop.setenv("DISPLAY", display_name, 1);
const magic_cookie = mcookie(); const mcookie_output = try mcookie(shell, mcookie_cmd);
const pid = try std.posix.fork(); const pid = try std.posix.fork();
if (pid == 0) { if (pid == 0) {
var cmd_buffer: [1024]u8 = undefined; var cmd_buffer: [1024]u8 = undefined;
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . {s}", .{ options.xauth_cmd, display_name, magic_cookie }) catch std.process.exit(1); const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . {s}", .{ xauth_cmd, display_name, mcookie_output }) catch std.process.exit(1);
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str }; const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
std.posix.execveZ(shell, &args, std.c.environ) catch {}; std.posix.execveZ(shell, &args, std.c.environ) catch {};
std.process.exit(1); std.process.exit(1);
@@ -391,35 +386,28 @@ fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, opti
if (status.status != 0) return error.XauthFailed; if (status.status != 0) return error.XauthFailed;
} }
fn executeShellCmd(shell: [*:0]const u8, options: AuthOptions) !void { fn executeShellCmd(shell: [*:0]const u8) !void {
// We don't want to redirect stdout and stderr in a shell session const args = [_:null]?[*:0]const u8{shell};
return std.posix.execveZ(shell, &args, std.c.environ);
}
fn executeWaylandCmd(shell: [*:0]const u8, wayland_cmd: []const u8, desktop_cmd: []const u8) !void {
var cmd_buffer: [1024]u8 = undefined; var cmd_buffer: [1024]u8 = undefined;
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", shell }); const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s}", .{ wayland_cmd, desktop_cmd });
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str }; const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
return std.posix.execveZ(shell, &args, std.c.environ); return std.posix.execveZ(shell, &args, std.c.environ);
} }
fn executeWaylandCmd(shell: [*:0]const u8, options: AuthOptions, desktop_cmd: []const u8) !void { fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, config: Config, desktop_cmd: []const u8, vt: []const u8) !void {
const log_file = try redirectStandardStreams(options.session_log, true);
defer log_file.close();
var cmd_buffer: [1024]u8 = undefined;
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", desktop_cmd });
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
return std.posix.execveZ(shell, &args, std.c.environ);
}
fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptions, desktop_cmd: []const u8, vt: []const u8) !void {
const display_num = try getFreeDisplay(); const display_num = try getFreeDisplay();
var buf: [5]u8 = undefined; var buf: [5]u8 = undefined;
const display_name = try std.fmt.bufPrintZ(&buf, ":{d}", .{display_num}); const display_name = try std.fmt.bufPrintZ(&buf, ":{d}", .{display_num});
try xauth(display_name, shell, pw_dir, options); try xauth(display_name, shell, pw_dir, config.xauth_cmd, config.mcookie_cmd);
const pid = try std.posix.fork(); const pid = try std.posix.fork();
if (pid == 0) { if (pid == 0) {
var cmd_buffer: [1024]u8 = undefined; var cmd_buffer: [1024]u8 = undefined;
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.x_cmd, display_name, vt }) catch std.process.exit(1); const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ config.x_cmd, display_name, vt }) catch std.process.exit(1);
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str }; const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
std.posix.execveZ(shell, &args, std.c.environ) catch {}; std.posix.execveZ(shell, &args, std.c.environ) catch {};
std.process.exit(1); std.process.exit(1);
@@ -442,7 +430,7 @@ fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptio
xorg_pid = try std.posix.fork(); xorg_pid = try std.posix.fork();
if (xorg_pid == 0) { if (xorg_pid == 0) {
var cmd_buffer: [1024]u8 = undefined; var cmd_buffer: [1024]u8 = undefined;
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", desktop_cmd }) catch std.process.exit(1); const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s}", .{ config.x_cmd_setup, desktop_cmd }) catch std.process.exit(1);
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str }; const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
std.posix.execveZ(shell, &args, std.c.environ) catch {}; std.posix.execveZ(shell, &args, std.c.environ) catch {};
std.process.exit(1); std.process.exit(1);
@@ -454,28 +442,18 @@ fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptio
.mask = std.posix.empty_sigset, .mask = std.posix.empty_sigset,
.flags = 0, .flags = 0,
}; };
std.posix.sigaction(std.posix.SIG.TERM, &act, null); try std.posix.sigaction(std.posix.SIG.TERM, &act, null);
_ = std.posix.waitpid(xorg_pid, 0); _ = std.posix.waitpid(xorg_pid, 0);
interop.xcb.xcb_disconnect(xcb); interop.xcb.xcb_disconnect(xcb);
std.posix.kill(x_pid, 0) catch return;
std.posix.kill(x_pid, std.posix.SIG.TERM) catch {}; std.posix.kill(x_pid, std.posix.SIG.TERM) catch {};
std.Thread.sleep(std.time.ns_per_s * 1); // Wait 1 second before sending SIGKILL
std.posix.kill(x_pid, std.posix.SIG.KILL) catch return;
var status: c_int = 0; var status: c_int = 0;
_ = std.c.waitpid(x_pid, &status, 0); _ = std.c.waitpid(x_pid, &status, 0);
} }
fn redirectStandardStreams(session_log: []const u8, create: bool) !std.fs.File {
const log_file = if (create) (try std.fs.cwd().createFile(session_log, .{ .mode = 0o666 })) else (try std.fs.cwd().openFile(session_log, .{ .mode = .read_write }));
try std.posix.dup2(std.posix.STDOUT_FILENO, std.posix.STDERR_FILENO);
try std.posix.dup2(log_file.handle, std.posix.STDOUT_FILENO);
return log_file;
}
fn addUtmpEntry(entry: *Utmp, username: [*:0]const u8, pid: c_int) !void { fn addUtmpEntry(entry: *Utmp, username: [*:0]const u8, pid: c_int) !void {
entry.ut_type = utmp.USER_PROCESS; entry.ut_type = utmp.USER_PROCESS;
entry.ut_pid = pid; entry.ut_pid = pid;
@@ -483,23 +461,23 @@ fn addUtmpEntry(entry: *Utmp, username: [*:0]const u8, pid: c_int) !void {
var buf: [4096]u8 = undefined; var buf: [4096]u8 = undefined;
const ttyname = try std.os.getFdPath(std.posix.STDIN_FILENO, &buf); const ttyname = try std.os.getFdPath(std.posix.STDIN_FILENO, &buf);
var ttyname_buf: [@sizeOf(@TypeOf(entry.ut_line))]u8 = undefined; var ttyname_buf: [32]u8 = undefined;
_ = try std.fmt.bufPrintZ(&ttyname_buf, "{s}", .{ttyname["/dev/".len..]}); _ = try std.fmt.bufPrintZ(&ttyname_buf, "{s}", .{ttyname["/dev/".len..]});
entry.ut_line = ttyname_buf; entry.ut_line = ttyname_buf;
entry.ut_id = ttyname_buf["tty".len..7].*; entry.ut_id = ttyname_buf["tty".len..7].*;
var username_buf: [@sizeOf(@TypeOf(entry.ut_user))]u8 = undefined; var username_buf: [32]u8 = undefined;
_ = try std.fmt.bufPrintZ(&username_buf, "{s}", .{username}); _ = try std.fmt.bufPrintZ(&username_buf, "{s}", .{username});
entry.ut_user = username_buf; entry.ut_user = username_buf;
var host: [@sizeOf(@TypeOf(entry.ut_host))]u8 = undefined; var host: [256]u8 = undefined;
host[0] = 0; host[0] = 0;
entry.ut_host = host; entry.ut_host = host;
var tv: interop.system_time.timeval = undefined; var tv: std.c.timeval = undefined;
_ = interop.system_time.gettimeofday(&tv, null); _ = std.c.gettimeofday(&tv, null);
entry.ut_tv = .{ entry.ut_tv = .{
.tv_sec = @intCast(tv.tv_sec), .tv_sec = @intCast(tv.tv_sec),
@@ -507,18 +485,18 @@ fn addUtmpEntry(entry: *Utmp, username: [*:0]const u8, pid: c_int) !void {
}; };
entry.ut_addr_v6[0] = 0; entry.ut_addr_v6[0] = 0;
utmp.setutxent(); utmp.setutent();
_ = utmp.pututxline(entry); _ = utmp.pututline(entry);
utmp.endutxent(); utmp.endutent();
} }
fn removeUtmpEntry(entry: *Utmp) void { fn removeUtmpEntry(entry: *Utmp) void {
entry.ut_type = utmp.DEAD_PROCESS; entry.ut_type = utmp.DEAD_PROCESS;
entry.ut_line[0] = 0; entry.ut_line[0] = 0;
entry.ut_user[0] = 0; entry.ut_user[0] = 0;
utmp.setutxent(); utmp.setutent();
_ = utmp.pututxline(entry); _ = utmp.pututline(entry);
utmp.endutxent(); utmp.endutent();
} }
fn pamDiagnose(status: c_int) anyerror { fn pamDiagnose(status: c_int) anyerror {

View File

@@ -1,57 +1,140 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin");
const interop = @import("interop.zig"); const interop = @import("interop.zig");
const enums = @import("enums.zig"); const utils = @import("tui/utils.zig");
const Lang = @import("bigclock/Lang.zig");
const en = @import("bigclock/en.zig");
const fa = @import("bigclock/fa.zig");
const Cell = @import("tui/Cell.zig");
const Bigclock = enums.Bigclock; const termbox = interop.termbox;
pub const WIDTH = Lang.WIDTH;
pub const HEIGHT = Lang.HEIGHT;
pub const SIZE = Lang.SIZE;
pub fn clockCell(animate: bool, char: u8, fg: u32, bg: u32, bigclock: Bigclock) [SIZE]Cell { const X: u32 = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) 0x2593 else '#';
var cells: [SIZE]Cell = undefined; const O: u32 = 0;
var tv: interop.system_time.timeval = undefined; pub const WIDTH = 5;
_ = interop.system_time.gettimeofday(&tv, null); pub const HEIGHT = 5;
pub const SIZE = WIDTH * HEIGHT;
const clock_chars = toBigNumber(if (animate and char == ':' and @divTrunc(tv.tv_usec, 500000) != 0) ' ' else char, bigclock); // zig fmt: off
for (0..cells.len) |i| cells[i] = Cell.init(clock_chars[i], fg, bg); const ZERO = [_]u21{
X,X,X,X,X,
X,X,O,X,X,
X,X,O,X,X,
X,X,O,X,X,
X,X,X,X,X,
};
const ONE = [_]u21{
O,O,O,X,X,
O,O,O,X,X,
O,O,O,X,X,
O,O,O,X,X,
O,O,O,X,X,
};
const TWO = [_]u21{
X,X,X,X,X,
O,O,O,X,X,
X,X,X,X,X,
X,X,O,O,O,
X,X,X,X,X,
};
const THREE = [_]u21{
X,X,X,X,X,
O,O,O,X,X,
X,X,X,X,X,
O,O,O,X,X,
X,X,X,X,X,
};
const FOUR = [_]u21{
X,X,O,X,X,
X,X,O,X,X,
X,X,X,X,X,
O,O,O,X,X,
O,O,O,X,X,
};
const FIVE = [_]u21{
X,X,X,X,X,
X,X,O,O,O,
X,X,X,X,X,
O,O,O,X,X,
X,X,X,X,X,
};
const SIX = [_]u21{
X,X,X,X,X,
X,X,O,O,O,
X,X,X,X,X,
X,X,O,X,X,
X,X,X,X,X,
};
const SEVEN = [_]u21{
X,X,X,X,X,
O,O,O,X,X,
O,O,O,X,X,
O,O,O,X,X,
O,O,O,X,X,
};
const EIGHT = [_]u21{
X,X,X,X,X,
X,X,O,X,X,
X,X,X,X,X,
X,X,O,X,X,
X,X,X,X,X,
};
const NINE = [_]u21{
X,X,X,X,X,
X,X,O,X,X,
X,X,X,X,X,
O,O,O,X,X,
X,X,X,X,X,
};
const S = [_]u21{
O,O,O,O,O,
O,O,X,O,O,
O,O,O,O,O,
O,O,X,O,O,
O,O,O,O,O,
};
const E = [_]u21{
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
};
// zig fmt: on
pub fn clockCell(animate: bool, char: u8, fg: u8, bg: u8) [SIZE]termbox.tb_cell {
var cells: [SIZE]termbox.tb_cell = undefined;
var tv: std.c.timeval = undefined;
_ = std.c.gettimeofday(&tv, null);
const clock_chars = toBigNumber(if (animate and char == ':' and @divTrunc(tv.tv_usec, 500000) != 0) ' ' else char);
for (0..cells.len) |i| cells[i] = utils.initCell(clock_chars[i], fg, bg);
return cells; return cells;
} }
pub fn alphaBlit(x: usize, y: usize, tb_width: usize, tb_height: usize, cells: [SIZE]Cell) void { pub fn alphaBlit(buffer: [*]termbox.tb_cell, x: u64, y: u64, tb_width: u64, tb_height: u64, cells: [SIZE]termbox.tb_cell) void {
if (x + WIDTH >= tb_width or y + HEIGHT >= tb_height) return; if (x + WIDTH >= tb_width or y + HEIGHT >= tb_height) return;
for (0..HEIGHT) |yy| { for (0..HEIGHT) |yy| {
for (0..WIDTH) |xx| { for (0..WIDTH) |xx| {
const cell = cells[yy * WIDTH + xx]; const cell = cells[yy * WIDTH + xx];
cell.put(x + xx, y + yy); if (cell.ch != 0) buffer[(y + yy) * tb_width + (x + xx)] = cell;
} }
} }
} }
fn toBigNumber(char: u8, bigclock: Bigclock) [SIZE]u21 { fn toBigNumber(char: u8) []const u21 {
const locale_chars = switch (bigclock) {
.fa => fa.locale_chars,
.en => en.locale_chars,
.none => unreachable,
};
return switch (char) { return switch (char) {
'0' => locale_chars.ZERO, '0' => &ZERO,
'1' => locale_chars.ONE, '1' => &ONE,
'2' => locale_chars.TWO, '2' => &TWO,
'3' => locale_chars.THREE, '3' => &THREE,
'4' => locale_chars.FOUR, '4' => &FOUR,
'5' => locale_chars.FIVE, '5' => &FIVE,
'6' => locale_chars.SIX, '6' => &SIX,
'7' => locale_chars.SEVEN, '7' => &SEVEN,
'8' => locale_chars.EIGHT, '8' => &EIGHT,
'9' => locale_chars.NINE, '9' => &NINE,
':' => locale_chars.S, ':' => &S,
else => locale_chars.E, else => &E,
}; };
} }

View File

@@ -1,23 +0,0 @@
const builtin = @import("builtin");
pub const WIDTH = 5;
pub const HEIGHT = 5;
pub const SIZE = WIDTH * HEIGHT;
pub const X: u32 = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) 0x2593 else '#';
pub const O: u32 = 0;
pub const LocaleChars = struct {
ZERO: [SIZE]u21,
ONE: [SIZE]u21,
TWO: [SIZE]u21,
THREE: [SIZE]u21,
FOUR: [SIZE]u21,
FIVE: [SIZE]u21,
SIX: [SIZE]u21,
SEVEN: [SIZE]u21,
EIGHT: [SIZE]u21,
NINE: [SIZE]u21,
S: [SIZE]u21,
E: [SIZE]u21,
};

View File

@@ -1,94 +0,0 @@
const Lang = @import("Lang.zig");
const LocaleChars = Lang.LocaleChars;
const X = Lang.X;
const O = Lang.O;
// zig fmt: off
pub const locale_chars = LocaleChars{
.ZERO = [_]u21{
X,X,X,X,X,
X,X,O,X,X,
X,X,O,X,X,
X,X,O,X,X,
X,X,X,X,X,
},
.ONE = [_]u21{
O,O,O,X,X,
O,O,O,X,X,
O,O,O,X,X,
O,O,O,X,X,
O,O,O,X,X,
},
.TWO = [_]u21{
X,X,X,X,X,
O,O,O,X,X,
X,X,X,X,X,
X,X,O,O,O,
X,X,X,X,X,
},
.THREE = [_]u21{
X,X,X,X,X,
O,O,O,X,X,
X,X,X,X,X,
O,O,O,X,X,
X,X,X,X,X,
},
.FOUR = [_]u21{
X,X,O,X,X,
X,X,O,X,X,
X,X,X,X,X,
O,O,O,X,X,
O,O,O,X,X,
},
.FIVE = [_]u21{
X,X,X,X,X,
X,X,O,O,O,
X,X,X,X,X,
O,O,O,X,X,
X,X,X,X,X,
},
.SIX = [_]u21{
X,X,X,X,X,
X,X,O,O,O,
X,X,X,X,X,
X,X,O,X,X,
X,X,X,X,X,
},
.SEVEN = [_]u21{
X,X,X,X,X,
O,O,O,X,X,
O,O,O,X,X,
O,O,O,X,X,
O,O,O,X,X,
},
.EIGHT = [_]u21{
X,X,X,X,X,
X,X,O,X,X,
X,X,X,X,X,
X,X,O,X,X,
X,X,X,X,X,
},
.NINE = [_]u21{
X,X,X,X,X,
X,X,O,X,X,
X,X,X,X,X,
O,O,O,X,X,
X,X,X,X,X,
},
.S = [_]u21{
O,O,O,O,O,
O,O,X,O,O,
O,O,O,O,O,
O,O,X,O,O,
O,O,O,O,O,
},
.E = [_]u21{
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
},
};
// zig fmt: on

View File

@@ -1,94 +0,0 @@
const Lang = @import("Lang.zig");
const LocaleChars = Lang.LocaleChars;
const X = Lang.X;
const O = Lang.O;
// zig fmt: off
pub const locale_chars = LocaleChars{
.ZERO = [_]u21{
O,O,O,O,O,
O,O,X,O,O,
O,X,O,X,O,
O,O,X,O,O,
O,O,O,O,O,
},
.ONE = [_]u21{
O,O,X,O,O,
O,X,X,O,O,
O,O,X,O,O,
O,O,X,O,O,
O,O,X,O,O,
},
.TWO = [_]u21{
O,X,O,X,O,
O,X,X,X,O,
O,X,O,O,O,
O,X,O,O,O,
O,X,O,O,O,
},
.THREE = [_]u21{
X,O,X,O,X,
X,X,X,X,X,
X,O,O,O,O,
X,O,O,O,O,
X,O,O,O,O,
},
.FOUR = [_]u21{
O,X,O,X,X,
O,X,X,O,O,
O,X,X,X,X,
O,X,O,O,O,
O,X,O,O,O,
},
.FIVE = [_]u21{
O,O,X,X,O,
O,X,O,O,X,
X,O,O,O,X,
X,O,X,O,X,
O,X,O,X,O,
},
.SIX = [_]u21{
O,X,X,O,O,
O,X,O,O,X,
O,O,X,O,O,
O,X,O,O,O,
X,O,O,O,O,
},
.SEVEN = [_]u21{
X,O,O,O,X,
X,O,O,O,X,
O,X,O,X,O,
O,X,O,X,O,
O,O,X,O,O,
},
.EIGHT = [_]u21{
O,O,O,X,O,
O,O,X,O,X,
O,O,X,O,X,
O,X,O,O,X,
O,X,O,O,X,
},
.NINE = [_]u21{
O,X,X,X,O,
O,X,O,X,O,
O,X,X,X,O,
O,O,O,X,O,
O,O,O,X,O,
},
.S = [_]u21{
O,O,O,O,O,
O,O,X,O,O,
O,O,O,O,O,
O,O,X,O,O,
O,O,O,O,O,
},
.E = [_]u21{
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
},
};
// zig fmt: on

View File

@@ -3,68 +3,51 @@ const enums = @import("../enums.zig");
const Animation = enums.Animation; const Animation = enums.Animation;
const Input = enums.Input; const Input = enums.Input;
const ViMode = enums.ViMode;
const Bigclock = enums.Bigclock;
allow_empty_password: bool = true,
animation: Animation = .none, animation: Animation = .none,
animation_timeout_sec: u12 = 0, asterisk: u8 = '*',
asterisk: ?u32 = '*', bg: u8 = 0,
auth_fails: u64 = 10, bigclock: bool = false,
bg: u32 = 0x00000000,
bigclock: Bigclock = .none,
blank_box: bool = true, blank_box: bool = true,
border_fg: u32 = 0x00FFFFFF, border_fg: u8 = 8,
box_title: ?[]const u8 = null, box_title: ?[]const u8 = null,
brightness_down_cmd: [:0]const u8 = build_options.prefix_directory ++ "/bin/brightnessctl -q s 10%-",
brightness_down_key: ?[]const u8 = "F5",
brightness_up_cmd: [:0]const u8 = build_options.prefix_directory ++ "/bin/brightnessctl -q s +10%",
brightness_up_key: ?[]const u8 = "F6",
clear_password: bool = false, clear_password: bool = false,
clock: ?[:0]const u8 = null, clock: ?[:0]const u8 = null,
cmatrix_fg: u32 = 0x0000FF00, console_dev: [:0]const u8 = "/dev/console",
cmatrix_min_codepoint: u16 = 0x21,
cmatrix_max_codepoint: u16 = 0x7B,
colormix_col1: u32 = 0x00FF0000,
colormix_col2: u32 = 0x000000FF,
colormix_col3: u32 = 0x20000000,
console_dev: []const u8 = "/dev/console",
default_input: Input = .login, default_input: Input = .login,
doom_top_color: u32 = 0x00FF0000, fg: u8 = 8,
doom_middle_color: u32 = 0x00FFFF00,
doom_bottom_color: u32 = 0x00FFFFFF,
error_bg: u32 = 0x00000000,
error_fg: u32 = 0x01FF0000,
fg: u32 = 0x00FFFFFF,
hide_borders: bool = false, hide_borders: bool = false,
hide_key_hints: bool = false, hide_key_hints: bool = false,
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",
load: bool = true, load: bool = true,
login_cmd: ?[]const u8 = null,
logout_cmd: ?[]const u8 = null,
margin_box_h: u8 = 2, margin_box_h: u8 = 2,
margin_box_v: u8 = 1, margin_box_v: u8 = 1,
max_desktop_len: u8 = 100,
max_login_len: u8 = 255,
max_password_len: u8 = 255,
mcookie_cmd: [:0]const u8 = "/usr/bin/mcookie",
min_refresh_delta: u16 = 5, min_refresh_delta: u16 = 5,
numlock: bool = false, numlock: bool = false,
path: ?[:0]const u8 = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", path: ?[:0]const u8 = "/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin",
restart_cmd: []const u8 = "/sbin/shutdown -r now", restart_cmd: []const u8 = "/sbin/shutdown -r now",
restart_key: []const u8 = "F2", restart_key: []const u8 = "F2",
save: bool = true, save: bool = true,
save_file: []const u8 = "/etc/ly/save",
service_name: [:0]const u8 = "ly", service_name: [:0]const u8 = "ly",
session_log: []const u8 = "ly-session.log",
setup_cmd: []const u8 = build_options.config_directory ++ "/ly/setup.sh",
shutdown_cmd: []const u8 = "/sbin/shutdown -a now", 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",
text_in_center: bool = false, term_reset_cmd: [:0]const u8 = "/usr/bin/tput reset",
tty: u8 = build_options.tty, term_restore_cursor_cmd: []const u8 = "/usr/bin/tput cnorm",
vi_default_mode: ViMode = .normal, tty: u8 = 2,
vi_mode: bool = false, vi_mode: bool = false,
waylandsessions: []const u8 = build_options.prefix_directory ++ "/share/wayland-sessions", wayland_cmd: []const u8 = build_options.data_directory ++ "/wsetup.sh",
x_cmd: []const u8 = build_options.prefix_directory ++ "/bin/X", waylandsessions: []const u8 = "/usr/share/wayland-sessions",
xauth_cmd: []const u8 = build_options.prefix_directory ++ "/bin/xauth", x_cmd: []const u8 = "/usr/bin/X",
xinitrc: ?[]const u8 = "~/.xinitrc", xinitrc: ?[]const u8 = "~/.xinitrc",
xsessions: []const u8 = build_options.prefix_directory ++ "/share/xsessions", x_cmd_setup: []const u8 = build_options.data_directory ++ "/xsetup.sh",
xauth_cmd: []const u8 = "/usr/bin/xauth",
xsessions: []const u8 = "/usr/share/xsessions",

View File

@@ -1,26 +1,16 @@
//
// NOTE: After editing this file, please run `/res/lang/normalize_lang_files.py`
// to update all the language files accordingly.
//
authenticating: []const u8 = "authenticating...", authenticating: []const u8 = "authenticating...",
brightness_down: []const u8 = "decrease brightness",
brightness_up: []const u8 = "increase brightness",
capslock: []const u8 = "capslock", capslock: []const u8 = "capslock",
err_alloc: []const u8 = "failed memory allocation", err_alloc: []const u8 = "failed memory allocation",
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_chdir: []const u8 = "failed to open home folder", err_chdir: []const u8 = "failed to open home folder",
err_config: []const u8 = "unable to parse config file",
err_console_dev: []const u8 = "failed to access console", err_console_dev: []const u8 = "failed to access console",
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_envlist: []const u8 = "failed to get envlist", err_envlist: []const u8 = "failed to get envlist",
err_hostname: []const u8 = "failed to get hostname", err_hostname: []const u8 = "failed to get hostname",
err_mcookie: []const u8 = "mcookie command failed",
err_mlock: []const u8 = "failed to lock password memory", err_mlock: []const u8 = "failed to lock password memory",
err_null: []const u8 = "null pointer", err_null: []const u8 = "null pointer",
err_numlock: []const u8 = "failed to set numlock",
err_pam: []const u8 = "pam transaction failed", err_pam: []const u8 = "pam transaction failed",
err_pam_abort: []const u8 = "pam transaction aborted", err_pam_abort: []const u8 = "pam transaction aborted",
err_pam_acct_expired: []const u8 = "account expired", err_pam_acct_expired: []const u8 = "account expired",
@@ -42,8 +32,6 @@ err_perm_dir: []const u8 = "failed to change current directory",
err_perm_group: []const u8 = "failed to downgrade group permissions", 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_tty_ctrl: []const u8 = "tty control transfer failed",
err_unknown: []const u8 = "an unknown error occurred", err_unknown: []const u8 = "an unknown error occurred",
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",
@@ -55,7 +43,6 @@ err_xsessions_open: []const u8 = "failed to open sessions folder",
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",
no_x11_support: []const u8 = "x11 support disabled at compile-time",
normal: []const u8 = "normal", normal: []const u8 = "normal",
numlock: []const u8 = "numlock", numlock: []const u8 = "numlock",
other: []const u8 = "other", other: []const u8 = "other",
@@ -65,5 +52,5 @@ shell: [:0]const u8 = "shell",
shutdown: []const u8 = "shutdown", shutdown: []const u8 = "shutdown",
sleep: []const u8 = "sleep", sleep: []const u8 = "sleep",
wayland: []const u8 = "wayland", wayland: []const u8 = "wayland",
x11: []const u8 = "x11",
xinitrc: [:0]const u8 = "xinitrc", xinitrc: [:0]const u8 = "xinitrc",
x11: []const u8 = "x11",

View File

@@ -1,2 +1,2 @@
user: ?[]const u8 = null, user: ?[]const u8 = null,
session_index: ?usize = null, session_index: ?u64 = null,

View File

@@ -3,201 +3,40 @@
const std = @import("std"); const std = @import("std");
const ini = @import("zigini"); const ini = @import("zigini");
const Save = @import("Save.zig"); const Save = @import("Save.zig");
const enums = @import("../enums.zig");
const color_properties = [_][]const u8{
"bg",
"border_fg",
"cmatrix_fg",
"colormix_col1",
"colormix_col2",
"colormix_col3",
"error_bg",
"error_fg",
"fg",
};
const removed_properties = [_][]const u8{
"wayland_specifier",
"max_desktop_len",
"max_login_len",
"max_password_len",
"mcookie_cmd",
"term_reset_cmd",
"term_restore_cursor_cmd",
"x_cmd_setup",
"wayland_cmd",
};
var temporary_allocator = std.heap.page_allocator;
var buffer = std.mem.zeroes([10 * color_properties.len]u8);
pub var maybe_animate: ?bool = null;
pub var maybe_save_file: ?[]const u8 = null;
pub var mapped_config_fields = false;
pub fn configFieldHandler(_: std.mem.Allocator, field: ini.IniField) ?ini.IniField { pub fn configFieldHandler(_: std.mem.Allocator, field: ini.IniField) ?ini.IniField {
if (std.mem.eql(u8, field.key, "animate")) { var mapped_field = field;
// The option doesn't exist anymore, but we save its value for "animation"
maybe_animate = std.mem.eql(u8, field.value, "true");
mapped_config_fields = true;
return null;
}
if (std.mem.eql(u8, field.key, "animation")) {
// The option now uses a string (which then gets converted into an enum) instead of an integer
// It also combines the previous "animate" and "animation" options
const animation = std.fmt.parseInt(u8, field.value, 10) catch return field;
var mapped_field = field;
mapped_field.value = switch (animation) {
0 => "doom",
1 => "matrix",
else => "none",
};
mapped_config_fields = true;
return mapped_field;
}
inline for (color_properties) |property| {
if (std.mem.eql(u8, field.key, property)) {
// These options now uses a 32-bit RGB value instead of an arbitrary 16-bit integer
const color = std.fmt.parseInt(u16, field.value, 0) catch return field;
var mapped_field = field;
mapped_field.value = mapColor(color) catch return field;
mapped_config_fields = true;
return mapped_field;
}
}
if (std.mem.eql(u8, field.key, "blank_password")) { if (std.mem.eql(u8, field.key, "blank_password")) {
// The option has simply been renamed
var mapped_field = field;
mapped_field.key = "clear_password"; mapped_field.key = "clear_password";
mapped_config_fields = true;
return mapped_field;
} }
if (std.mem.eql(u8, field.key, "default_input")) { return mapped_field;
// The option now uses a string (which then gets converted into an enum) instead of an integer
const default_input = std.fmt.parseInt(u8, field.value, 10) catch return field;
var mapped_field = field;
mapped_field.value = switch (default_input) {
0 => "session",
1 => "login",
2 => "password",
else => "login",
};
mapped_config_fields = true;
return mapped_field;
}
if (std.mem.eql(u8, field.key, "save_file")) {
// The option doesn't exist anymore, but we save its value for migration later on
maybe_save_file = temporary_allocator.dupe(u8, field.value) catch return null;
mapped_config_fields = true;
return null;
}
inline for (removed_properties) |property| {
if (std.mem.eql(u8, field.key, property)) {
// The options don't exist anymore
mapped_config_fields = true;
return null;
}
}
if (std.mem.eql(u8, field.key, "bigclock")) {
// The option now uses a string (which then gets converted into an enum) instead of an boolean
// It also includes the ability to change active bigclock's language
var mapped_field = field;
if (std.mem.eql(u8, field.value, "true")) {
mapped_field.value = "en";
mapped_config_fields = true;
} else if (std.mem.eql(u8, field.value, "false")) {
mapped_field.value = "none";
mapped_config_fields = true;
}
return mapped_field;
}
return field;
} }
// This is the stuff we only handle after reading the config. pub fn tryMigrateSaveFile(user_buf: *[32]u8, path: []const u8) Save {
// For example, the "animate" field could come after "animation"
pub fn lateConfigFieldHandler(animation: *enums.Animation) void {
if (maybe_animate) |animate| {
if (!animate) animation.* = .none;
}
}
pub fn tryMigrateSaveFile(user_buf: *[32]u8) Save {
var save = Save{}; var save = Save{};
if (maybe_save_file) |path| { var file = std.fs.openFileAbsolute(path, .{}) catch return save;
defer temporary_allocator.free(path); defer file.close();
var file = std.fs.openFileAbsolute(path, .{}) catch return save; const reader = file.reader();
defer file.close();
const reader = file.reader(); var user_fbs = std.io.fixedBufferStream(user_buf);
reader.streamUntilDelimiter(user_fbs.writer(), '\n', 32) catch return save;
const user = user_fbs.getWritten();
if (user.len > 0) save.user = user;
var user_fbs = std.io.fixedBufferStream(user_buf); var session_buf: [20]u8 = undefined;
reader.streamUntilDelimiter(user_fbs.writer(), '\n', user_buf.len) catch return save; var session_fbs = std.io.fixedBufferStream(&session_buf);
const user = user_fbs.getWritten(); reader.streamUntilDelimiter(session_fbs.writer(), '\n', 20) catch {};
if (user.len > 0) save.user = user;
var session_buf: [20]u8 = undefined; const session_index_str = session_fbs.getWritten();
var session_fbs = std.io.fixedBufferStream(&session_buf); var session_index: ?u64 = null;
reader.streamUntilDelimiter(session_fbs.writer(), '\n', session_buf.len) catch return save; if (session_index_str.len > 0) {
session_index = std.fmt.parseUnsigned(u64, session_index_str, 10) catch return save;
const session_index_str = session_fbs.getWritten();
var session_index: ?usize = null;
if (session_index_str.len > 0) {
session_index = std.fmt.parseUnsigned(usize, session_index_str, 10) catch return save;
}
save.session_index = session_index;
} }
save.session_index = session_index;
return save; return save;
} }
fn mapColor(color: u16) ![]const u8 {
const color_no_styling = color & 0x00FF;
const styling_only = color & 0xFF00;
// If color is "greater" than TB_WHITE, or the styling is "greater" than TB_DIM,
// we have an invalid color, so return an error
if (color_no_styling > 0x0008 or styling_only > 0x8000) return error.InvalidColor;
var new_color: u32 = switch (color_no_styling) {
0x0000 => 0x00000000, // Default
0x0001 => 0x20000000, // "Hi-black" styling
0x0002 => 0x00FF0000, // Red
0x0003 => 0x0000FF00, // Green
0x0004 => 0x00FFFF00, // Yellow
0x0005 => 0x000000FF, // Blue
0x0006 => 0x00FF00FF, // Magenta
0x0007 => 0x0000FFFF, // Cyan
0x0008 => 0x00FFFFFF, // White
else => unreachable,
};
// Only applying styling if color isn't black and styling isn't also black
if (!(new_color == 0x20000000 and styling_only == 0x20000000)) {
// Shift styling by 16 to the left to apply it to the new 32-bit color
new_color |= @as(u32, @intCast(styling_only)) << 16;
}
return try std.fmt.bufPrint(&buffer, "0x{X}", .{new_color});
}

View File

@@ -2,7 +2,6 @@ pub const Animation = enum {
none, none,
doom, doom,
matrix, matrix,
colormix,
}; };
pub const DisplayServer = enum { pub const DisplayServer = enum {
@@ -13,19 +12,7 @@ pub const DisplayServer = enum {
}; };
pub const Input = enum { pub const Input = enum {
info_line,
session, session,
login, login,
password, password,
}; };
pub const ViMode = enum {
normal,
insert,
};
pub const Bigclock = enum {
none,
en,
fa,
};

View File

@@ -9,101 +9,111 @@ pub const pam = @cImport({
}); });
pub const utmp = @cImport({ pub const utmp = @cImport({
@cInclude("utmpx.h"); @cInclude("utmp.h");
}); });
// Exists for X11 support only
pub const xcb = @cImport({ pub const xcb = @cImport({
@cInclude("xcb/xcb.h"); @cInclude("xcb/xcb.h");
}); });
pub const unistd = @cImport({ pub const c_size = u64;
@cInclude("unistd.h"); pub const c_uid = u32;
}); pub const c_gid = u32;
pub const c_time = c_long;
pub const tm = extern struct {
tm_sec: c_int,
tm_min: c_int,
tm_hour: c_int,
tm_mday: c_int,
tm_mon: c_int,
tm_year: c_int,
tm_wday: c_int,
tm_yday: c_int,
tm_isdst: c_int,
};
pub const passwd = extern struct {
pw_name: [*:0]u8,
pw_passwd: [*:0]u8,
pub const time = @cImport({ pw_uid: c_uid,
@cInclude("time.h"); pw_gid: c_gid,
}); pw_gecos: [*:0]u8,
pw_dir: [*:0]u8,
pw_shell: [*:0]u8,
};
pub const system_time = @cImport({ pub const VT_ACTIVATE: c_int = 0x5606;
@cInclude("sys/time.h"); pub const VT_WAITACTIVE: c_int = 0x5607;
});
pub const stdlib = @cImport({ pub const KDGETLED: c_int = 0x4B31;
@cInclude("stdlib.h"); pub const KDSETLED: c_int = 0x4B32;
}); pub const KDGKBLED: c_int = 0x4B64;
pub const KDSKBLED: c_int = 0x4B65;
pub const pwd = @cImport({ pub const LED_NUM: c_int = 0x02;
@cInclude("pwd.h"); pub const LED_CAP: c_int = 0x04;
// We include a FreeBSD-specific header here since login_cap.h references
// the passwd struct directly, so we can't import it separately'
if (builtin.os.tag == .freebsd) @cInclude("login_cap.h");
});
pub const grp = @cImport({ pub const K_NUMLOCK: c_int = 0x02;
@cInclude("grp.h"); pub const K_CAPSLOCK: c_int = 0x04;
});
// BSD-specific headers pub extern "c" fn localtime(timer: *const c_time) *tm;
pub const kbio = @cImport({ pub extern "c" fn strftime(str: [*:0]u8, maxsize: c_size, format: [*:0]const u8, timeptr: *const tm) c_size;
@cInclude("sys/kbio.h"); pub extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int;
}); pub extern "c" fn putenv(name: [*:0]u8) c_int;
pub extern "c" fn getuid() c_uid;
pub extern "c" fn getpwnam(name: [*:0]const u8) ?*passwd;
pub extern "c" fn endpwent() void;
pub extern "c" fn setusershell() void;
pub extern "c" fn getusershell() [*:0]u8;
pub extern "c" fn endusershell() void;
pub extern "c" fn initgroups(user: [*:0]const u8, group: c_gid) c_int;
// Linux-specific headers pub fn timeAsString(buf: [:0]u8, format: [:0]const u8) ![]u8 {
pub const kd = @cImport({
@cInclude("sys/kd.h");
});
pub const vt = @cImport({
@cInclude("sys/vt.h");
});
// Used for getting & setting the lock state
const LedState = if (builtin.os.tag.isBSD()) c_int else c_char;
const get_led_state = if (builtin.os.tag.isBSD()) kbio.KDGETLED else kd.KDGKBLED;
const set_led_state = if (builtin.os.tag.isBSD()) kbio.KDSETLED else kd.KDSKBLED;
const numlock_led = if (builtin.os.tag.isBSD()) kbio.LED_NUM else kd.K_NUMLOCK;
const capslock_led = if (builtin.os.tag.isBSD()) kbio.LED_CAP else kd.K_CAPSLOCK;
pub fn timeAsString(buf: [:0]u8, format: [:0]const u8) []u8 {
const timer = std.time.timestamp(); const timer = std.time.timestamp();
const tm_info = time.localtime(&timer); const tm_info = localtime(&timer);
const len = time.strftime(buf, buf.len, format, tm_info);
const len = strftime(buf, buf.len, format, tm_info);
if (len < 0) return error.CannotGetFormattedTime;
return buf[0..len]; return buf[0..len];
} }
pub fn switchTty(console_dev: []const u8, tty: u8) !void { pub fn getLockState(console_dev: [:0]const u8) !struct {
const fd = try std.posix.open(console_dev, .{ .ACCMODE = .WRONLY }, 0);
defer std.posix.close(fd);
_ = std.c.ioctl(fd, vt.VT_ACTIVATE, tty);
_ = std.c.ioctl(fd, vt.VT_WAITACTIVE, tty);
}
pub fn getLockState(console_dev: []const u8) !struct {
numlock: bool, numlock: bool,
capslock: bool, capslock: bool,
} { } {
const fd = try std.posix.open(console_dev, .{ .ACCMODE = .RDONLY }, 0); const fd = std.c.open(console_dev, .{ .ACCMODE = .RDONLY });
defer std.posix.close(fd); if (fd < 0) return error.CannotOpenConsoleDev;
defer _ = std.c.close(fd);
var led: LedState = undefined; var numlock = false;
_ = std.c.ioctl(fd, get_led_state, &led); var capslock = false;
if (builtin.os.tag.isBSD()) {
var led: c_int = undefined;
_ = std.c.ioctl(fd, KDGETLED, &led);
numlock = (led & LED_NUM) != 0;
capslock = (led & LED_CAP) != 0;
} else {
var led: c_char = undefined;
_ = std.c.ioctl(fd, KDGKBLED, &led);
numlock = (led & K_NUMLOCK) != 0;
capslock = (led & K_CAPSLOCK) != 0;
}
return .{ return .{
.numlock = (led & numlock_led) != 0, .numlock = numlock,
.capslock = (led & capslock_led) != 0, .capslock = capslock,
}; };
} }
pub fn setNumlock(val: bool) !void { pub fn setNumlock(val: bool) !void {
var led: LedState = undefined; var led: c_char = undefined;
_ = std.c.ioctl(0, get_led_state, &led); _ = std.c.ioctl(0, KDGKBLED, &led);
const numlock = (led & numlock_led) != 0; const numlock = (led & K_NUMLOCK) != 0;
if (numlock != val) { if (numlock != val) {
const status = std.c.ioctl(std.posix.STDIN_FILENO, set_led_state, led ^ numlock_led); const status = std.c.ioctl(std.posix.STDIN_FILENO, KDSKBLED, led ^ K_NUMLOCK);
if (status != 0) return error.FailedToSetNumlock; if (status != 0) return error.FailedToSetNumlock;
} }
} }

View File

@@ -5,16 +5,11 @@ const clap = @import("clap");
const ini = @import("zigini"); const ini = @import("zigini");
const auth = @import("auth.zig"); const auth = @import("auth.zig");
const bigclock = @import("bigclock.zig"); const bigclock = @import("bigclock.zig");
const enums = @import("enums.zig");
const Environment = @import("Environment.zig");
const interop = @import("interop.zig"); const interop = @import("interop.zig");
const ColorMix = @import("animations/ColorMix.zig");
const Doom = @import("animations/Doom.zig"); const Doom = @import("animations/Doom.zig");
const Dummy = @import("animations/Dummy.zig");
const Matrix = @import("animations/Matrix.zig"); const Matrix = @import("animations/Matrix.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 Desktop = @import("tui/components/Desktop.zig");
const Text = @import("tui/components/Text.zig"); const Text = @import("tui/components/Text.zig");
const InfoLine = @import("tui/components/InfoLine.zig"); const InfoLine = @import("tui/components/InfoLine.zig");
const Config = @import("config/Config.zig"); const Config = @import("config/Config.zig");
@@ -22,17 +17,14 @@ const Lang = @import("config/Lang.zig");
const Save = @import("config/Save.zig"); const Save = @import("config/Save.zig");
const migrator = @import("config/migrator.zig"); const migrator = @import("config/migrator.zig");
const SharedError = @import("SharedError.zig"); const SharedError = @import("SharedError.zig");
const utils = @import("tui/utils.zig");
const Ini = ini.Ini; const Ini = ini.Ini;
const DisplayServer = enums.DisplayServer;
const Entry = Environment.Entry;
const termbox = interop.termbox; const termbox = interop.termbox;
const unistd = interop.unistd;
const temporary_allocator = std.heap.page_allocator; const temporary_allocator = std.heap.page_allocator;
const ly_top_str = "Ly version " ++ build_options.version;
var session_pid: std.posix.pid_t = -1; var session_pid: std.posix.pid_t = -1;
fn signalHandler(i: c_int) callconv(.C) void { pub fn signalHandler(i: c_int) callconv(.C) void {
if (session_pid == 0) return; if (session_pid == 0) return;
// Forward signal to session to clean up // Forward signal to session to clean up
@@ -46,10 +38,6 @@ fn signalHandler(i: c_int) callconv(.C) void {
std.c.exit(i); std.c.exit(i);
} }
fn ttyControlTransferSignalHandler(_: c_int) callconv(.C) void {
_ = termbox.tb_shutdown();
}
pub fn main() !void { pub fn main() !void {
var shutdown = false; var shutdown = false;
var restart = false; var restart = false;
@@ -73,14 +61,9 @@ pub fn main() !void {
} }
} }
var gpa = std.heap.DebugAllocator(.{}).init; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit(); defer _ = gpa.deinit();
// Allows stopping an animation after some time
var tv_zero: interop.system_time.timeval = undefined;
_ = interop.system_time.gettimeofday(&tv_zero, null);
var animation_timed_out: bool = false;
const allocator = gpa.allocator(); const allocator = gpa.allocator();
// Load arguments // Load arguments
@@ -100,13 +83,12 @@ pub fn main() !void {
var config: Config = undefined; var config: Config = undefined;
var lang: Lang = undefined; var lang: Lang = undefined;
var save: Save = undefined; var save: Save = undefined;
var config_load_failed = false; var info_line = InfoLine{};
var can_draw_clock = true;
if (res.args.help != 0) { if (res.args.help != 0) {
try clap.help(stderr, clap.Help, &params, .{}); 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 usually located at /etc/ly/config.ini.\n");
std.process.exit(0); std.process.exit(0);
} }
if (res.args.version != 0) { if (res.args.version != 0) {
@@ -124,7 +106,7 @@ pub fn main() !void {
var save_ini = Ini(Save).init(allocator); var save_ini = Ini(Save).init(allocator);
defer save_ini.deinit(); defer save_ini.deinit();
var save_path: []const u8 = build_options.config_directory ++ "/ly/save.ini"; var save_path: []const u8 = build_options.data_directory ++ "/save.ini";
var save_path_alloc = false; var save_path_alloc = false;
defer { defer {
if (save_path_alloc) allocator.free(save_path); if (save_path_alloc) allocator.free(save_path);
@@ -138,79 +120,55 @@ pub fn main() !void {
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 });
defer allocator.free(config_path); defer allocator.free(config_path);
config = config_ini.readFileToStruct(config_path, .{ config = config_ini.readFileToStruct(config_path, comment_characters, migrator.configFieldHandler) catch Config{};
.fieldHandler = migrator.configFieldHandler,
.comment_characters = comment_characters,
}) catch _config: {
config_load_failed = true;
break :_config Config{};
};
const lang_path = try std.fmt.allocPrint(allocator, "{s}{s}lang/{s}.ini", .{ s, trailing_slash, config.lang }); const lang_path = try std.fmt.allocPrint(allocator, "{s}{s}lang/{s}.ini", .{ s, trailing_slash, config.lang });
defer allocator.free(lang_path); defer allocator.free(lang_path);
lang = lang_ini.readFileToStruct(lang_path, .{ lang = lang_ini.readFileToStruct(lang_path, comment_characters, null) catch Lang{};
.fieldHandler = null,
.comment_characters = comment_characters,
}) catch Lang{};
if (config.load) { if (config.load) {
save_path = try std.fmt.allocPrint(allocator, "{s}{s}save.ini", .{ s, trailing_slash }); save_path = try std.fmt.allocPrint(allocator, "{s}{s}save.ini", .{ s, trailing_slash });
save_path_alloc = true; save_path_alloc = true;
var user_buf: [32]u8 = undefined; var user_buf: [32]u8 = undefined;
save = save_ini.readFileToStruct(save_path, .{ save = save_ini.readFileToStruct(save_path, comment_characters, null) catch migrator.tryMigrateSaveFile(&user_buf, config.save_file);
.fieldHandler = null,
.comment_characters = comment_characters,
}) catch migrator.tryMigrateSaveFile(&user_buf);
} }
migrator.lateConfigFieldHandler(&config.animation);
} else { } else {
const config_path = build_options.config_directory ++ "/ly/config.ini"; const config_path = build_options.data_directory ++ "/config.ini";
config = config_ini.readFileToStruct(config_path, .{ config = config_ini.readFileToStruct(config_path, comment_characters, migrator.configFieldHandler) catch Config{};
.fieldHandler = migrator.configFieldHandler,
.comment_characters = comment_characters,
}) catch _config: {
config_load_failed = true;
break :_config Config{};
};
const lang_path = try std.fmt.allocPrint(allocator, "{s}/ly/lang/{s}.ini", .{ build_options.config_directory, config.lang }); const lang_path = try std.fmt.allocPrint(allocator, "{s}/lang/{s}.ini", .{ build_options.data_directory, config.lang });
defer allocator.free(lang_path); defer allocator.free(lang_path);
lang = lang_ini.readFileToStruct(lang_path, .{ lang = lang_ini.readFileToStruct(lang_path, comment_characters, null) catch Lang{};
.fieldHandler = null,
.comment_characters = comment_characters,
}) catch Lang{};
if (config.load) { if (config.load) {
var user_buf: [32]u8 = undefined; var user_buf: [32]u8 = undefined;
save = save_ini.readFileToStruct(save_path, .{ save = save_ini.readFileToStruct(save_path, comment_characters, null) catch migrator.tryMigrateSaveFile(&user_buf, config.save_file);
.fieldHandler = null,
.comment_characters = comment_characters,
}) catch migrator.tryMigrateSaveFile(&user_buf);
} }
migrator.lateConfigFieldHandler(&config.animation);
} }
// if (migrator.mapped_config_fields) save_migrated_config: {
// var file = try std.fs.cwd().createFile(config_path, .{});
// defer file.close();
// const writer = file.writer();
// ini.writeFromStruct(config, writer, null, true, .{}) catch {
// break :save_migrated_config;
// };
// }
// These strings only end up getting freed if the user quits Ly using Ctrl+C, which is fine since in the other cases // These strings only end up getting freed if the user quits Ly using Ctrl+C, which is fine since in the other cases
// we end up shutting down or restarting the system // we end up shutting down or restarting the system
shutdown_cmd = try temporary_allocator.dupe(u8, config.shutdown_cmd); shutdown_cmd = try temporary_allocator.dupe(u8, config.shutdown_cmd);
restart_cmd = try temporary_allocator.dupe(u8, config.restart_cmd); restart_cmd = try temporary_allocator.dupe(u8, config.restart_cmd);
interop.setNumlock(config.numlock) catch {};
if (config.initial_info_text) |text| {
try info_line.setText(text);
} 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 {
try info_line.setText(lang.err_hostname);
break :get_host_name;
};
try info_line.setText(hostname);
}
// Initialize termbox // Initialize termbox
_ = termbox.tb_init(); _ = termbox.tb_init();
defer _ = termbox.tb_shutdown(); defer _ = termbox.tb_shutdown();
@@ -220,9 +178,9 @@ pub fn main() !void {
.mask = std.posix.empty_sigset, .mask = std.posix.empty_sigset,
.flags = 0, .flags = 0,
}; };
std.posix.sigaction(std.posix.SIG.TERM, &act, null); try std.posix.sigaction(std.posix.SIG.TERM, &act, null);
_ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_TRUECOLOR); _ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_NORMAL);
_ = termbox.tb_clear(); _ = termbox.tb_clear();
// Needed to reset termbox after auth // Needed to reset termbox after auth
@@ -231,95 +189,51 @@ pub fn main() !void {
// Initialize terminal buffer // Initialize terminal buffer
const labels_max_length = @max(lang.login.len, lang.password.len); const labels_max_length = @max(lang.login.len, lang.password.len);
// Get a random seed for the PRNG (used by animations)
var seed: u64 = undefined; var seed: u64 = undefined;
std.crypto.random.bytes(std.mem.asBytes(&seed)); // Get a random seed for the PRNG (used by animations) try std.posix.getrandom(std.mem.asBytes(&seed));
var prng = std.Random.DefaultPrng.init(seed); var prng = std.Random.DefaultPrng.init(seed);
const random = prng.random(); const random = prng.random();
const buffer_options = TerminalBuffer.InitOptions{ var buffer = TerminalBuffer.init(config, labels_max_length, random);
.fg = config.fg,
.bg = config.bg,
.border_fg = config.border_fg,
.margin_box_h = config.margin_box_h,
.margin_box_v = config.margin_box_v,
.input_len = config.input_len,
};
var buffer = TerminalBuffer.init(buffer_options, labels_max_length, random);
// Initialize components // Initialize components
var info_line = InfoLine.init(allocator, &buffer); var desktop = try Desktop.init(allocator, &buffer, config.max_desktop_len, lang);
defer info_line.deinit(); defer desktop.deinit();
if (config_load_failed) { desktop.addEnvironment(.{ .Name = lang.shell }, "", .shell) catch {
// We can't localize this since the config failed to load so we'd fallback to the default language anyway try info_line.setText(lang.err_alloc);
try info_line.addMessage("unable to parse config file", config.error_bg, config.error_fg);
}
interop.setNumlock(config.numlock) catch {
try info_line.addMessage(lang.err_numlock, config.error_bg, config.error_fg);
}; };
if (config.xinitrc) |xinitrc| {
var session = Session.init(allocator, &buffer); desktop.addEnvironment(.{ .Name = lang.xinitrc, .Exec = xinitrc }, "", .xinitrc) catch {
defer session.deinit(); try info_line.setText(lang.err_alloc);
addOtherEnvironment(&session, lang, .shell, null) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
};
if (build_options.enable_x11_support) {
if (config.xinitrc) |xinitrc_cmd| {
addOtherEnvironment(&session, lang, .xinitrc, xinitrc_cmd) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
};
}
} else {
try info_line.addMessage(lang.no_x11_support, config.bg, config.fg);
}
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 {
try info_line.addMessage(lang.err_hostname, config.error_bg, config.error_fg);
break :get_host_name;
}; };
try info_line.addMessage(hostname, config.bg, config.fg);
} }
var wayland_session_dirs = std.mem.splitScalar(u8, config.waylandsessions, ':'); try desktop.crawl(config.waylandsessions, .wayland);
while (wayland_session_dirs.next()) |dir| { try desktop.crawl(config.xsessions, .x11);
try crawl(&session, lang, dir, .wayland);
}
if (build_options.enable_x11_support) {
var x_session_dirs = std.mem.splitScalar(u8, config.xsessions, ':');
while (x_session_dirs.next()) |dir| {
try crawl(&session, lang, dir, .x11);
}
}
var login = Text.init(allocator, &buffer, false, null); var login = try Text.init(allocator, &buffer, config.max_login_len);
defer login.deinit(); defer login.deinit();
var password = Text.init(allocator, &buffer, true, config.asterisk); var password = try Text.init(allocator, &buffer, config.max_password_len);
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;
// Load last saved username and desktop selection, if any // Load last saved username and desktop selection, if any
if (config.load) { if (config.load) {
if (save.user) |user| { if (save.user) |user| {
try login.text.appendSlice(login.allocator, user); try login.text.appendSlice(user);
login.end = user.len; login.end = user.len;
login.cursor = login.end; login.cursor = login.end;
active_input = .password; active_input = .password;
} }
if (save.session_index) |session_index| { if (save.session_index) |session_index| {
if (session_index < session.label.list.items.len) session.label.current = session_index; if (session_index < desktop.environments.items.len) desktop.current = session_index;
} }
} }
@@ -328,116 +242,125 @@ pub fn main() !void {
buffer.drawBoxCenter(!config.hide_borders, config.blank_box); buffer.drawBoxCenter(!config.hide_borders, config.blank_box);
const coordinates = buffer.calculateComponentCoordinates(); const coordinates = buffer.calculateComponentCoordinates();
info_line.label.position(coordinates.start_x, coordinates.y, coordinates.full_visible_length, null); desktop.position(coordinates.x, coordinates.y + 2, coordinates.visible_length);
session.label.position(coordinates.x, coordinates.y + 2, coordinates.visible_length, config.text_in_center);
login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length); login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length);
password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length); password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length);
switch (active_input) { switch (active_input) {
.info_line => info_line.label.handle(null, insert_mode), .session => desktop.handle(null, insert_mode),
.session => session.label.handle(null, insert_mode),
.login => login.handle(null, insert_mode) catch { .login => login.handle(null, insert_mode) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); try info_line.setText(lang.err_alloc);
}, },
.password => password.handle(null, insert_mode) catch { .password => password.handle(null, insert_mode) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); try info_line.setText(lang.err_alloc);
}, },
} }
} }
// Initialize the animation, if any // Initialize the animation, if any
var animation: Animation = undefined; var doom: Doom = undefined;
var matrix: Matrix = undefined;
switch (config.animation) { switch (config.animation) {
.none => { .none => {},
var dummy = Dummy{}; .doom => doom = try Doom.init(allocator, &buffer),
animation = dummy.animation(); .matrix => matrix = try Matrix.init(allocator, &buffer),
}, }
.doom => { defer {
var doom = try Doom.init(allocator, &buffer, config.doom_top_color, config.doom_middle_color, config.doom_bottom_color); switch (config.animation) {
animation = doom.animation(); .none => {},
}, .doom => doom.deinit(),
.matrix => { .matrix => matrix.deinit(),
var matrix = try Matrix.init(allocator, &buffer, config.cmatrix_fg, config.cmatrix_min_codepoint, config.cmatrix_max_codepoint); }
animation = matrix.animation();
},
.colormix => {
var color_mix = ColorMix.init(&buffer, config.colormix_col1, config.colormix_col2, config.colormix_col3);
animation = color_mix.animation();
},
} }
defer animation.deinit();
const animate = config.animation != .none; const animate = config.animation != .none;
const shutdown_key = try std.fmt.parseInt(u8, config.shutdown_key[1..], 10); const shutdown_key = try std.fmt.parseInt(u8, config.shutdown_key[1..], 10);
const shutdown_len = try TerminalBuffer.strWidth(lang.shutdown); const shutdown_len = try utils.strWidth(lang.shutdown);
const restart_key = try std.fmt.parseInt(u8, config.restart_key[1..], 10); const restart_key = try std.fmt.parseInt(u8, config.restart_key[1..], 10);
const restart_len = try TerminalBuffer.strWidth(lang.restart); const restart_len = try utils.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 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_up_key = if (config.brightness_up_key) |key| try std.fmt.parseInt(u8, key[1..], 10) else null;
const brightness_up_len = try TerminalBuffer.strWidth(lang.brightness_up);
var event: termbox.tb_event = undefined; var event: termbox.tb_event = undefined;
var run = true; var run = true;
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 can_access_console_dev = true;
// Switch to selected TTY if possible // Switch to selected TTY if possible
interop.switchTty(config.console_dev, config.tty) catch { open_console_dev: {
try info_line.addMessage(lang.err_console_dev, config.error_bg, config.error_fg); const fd = std.posix.open(config.console_dev, .{ .ACCMODE = .WRONLY }, 0) catch {
can_access_console_dev = false; try info_line.setText(lang.err_console_dev);
}; break :open_console_dev;
};
defer std.posix.close(fd);
_ = std.c.ioctl(fd, interop.VT_ACTIVATE, config.tty);
_ = std.c.ioctl(fd, interop.VT_WAITACTIVE, config.tty);
}
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 config.animation != .none) {
if (!update) std.Thread.sleep(std.time.ns_per_ms * 100); if (!update) std.time.sleep(std.time.ns_per_ms * 100);
_ = termbox.tb_present(); // Required to update tb_width() and tb_height() _ = termbox.tb_present(); // Required to update tb_width(), tb_height() and tb_cell_buffer()
const width: usize = @intCast(termbox.tb_width()); const width: u64 = @intCast(termbox.tb_width());
const height: usize = @intCast(termbox.tb_height()); const height: u64 = @intCast(termbox.tb_height());
if (width != buffer.width or height != buffer.height) {
// If it did change, then update the cell buffer, reallocate the current animation's buffers, and force a draw update
if (width != buffer.width) {
buffer.width = width; buffer.width = width;
buffer.height = height; resolution_changed = true;
}
animation.realloc() catch { if (height != buffer.height) {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); buffer.height = height;
}; resolution_changed = true;
}
// If it did change, then update the cell buffer, reallocate the current animation's buffers, and force a draw update
if (resolution_changed) {
buffer.buffer = termbox.tb_cell_buffer();
switch (config.animation) {
.none => {},
.doom => doom.realloc() catch {
try info_line.setText(lang.err_alloc);
},
.matrix => matrix.realloc() catch {
try info_line.setText(lang.err_alloc);
},
}
update = true; update = true;
resolution_changed = true;
} }
} }
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 (auth_fails < 10) {
_ = termbox.tb_clear(); _ = termbox.tb_clear();
if (!animation_timed_out) animation.draw(); switch (config.animation) {
.none => {},
.doom => doom.draw(),
.matrix => matrix.draw(),
}
buffer.drawLabel(ly_top_str, 0, 0); if (config.bigclock and buffer.box_height + (bigclock.HEIGHT + 2) * 2 < buffer.height) draw_big_clock: {
if (config.bigclock != .none and buffer.box_height + (bigclock.HEIGHT + 2) * 2 < buffer.height) {
const format = "%H:%M"; const format = "%H:%M";
const xo = buffer.width / 2 - @min(buffer.width, (format.len * (bigclock.WIDTH + 1))) / 2; const xo = buffer.width / 2 - @min(buffer.width, (format.len * (bigclock.WIDTH + 1))) / 2;
const yo = (buffer.height - buffer.box_height) / 2 - bigclock.HEIGHT - 2; const yo = (buffer.height - buffer.box_height) / 2 - bigclock.HEIGHT - 2;
var clock_buf: [format.len + 1:0]u8 = undefined; var clock_buf: [format.len + 1:0]u8 = undefined;
const clock_str = interop.timeAsString(&clock_buf, format); const clock_str = interop.timeAsString(&clock_buf, format) catch {
break :draw_big_clock;
};
for (clock_str, 0..) |c, i| { for (clock_str, 0..) |c, i| {
const clock_cell = bigclock.clockCell(animate, c, buffer.fg, buffer.bg, config.bigclock); const clock_cell = bigclock.clockCell(animate, c, buffer.fg, buffer.bg);
bigclock.alphaBlit(xo + i * (bigclock.WIDTH + 1), yo, buffer.width, buffer.height, clock_cell); bigclock.alphaBlit(buffer.buffer, xo + i * (bigclock.WIDTH + 1), yo, buffer.width, buffer.height, clock_cell);
} }
} }
@@ -445,8 +368,7 @@ pub fn main() !void {
if (resolution_changed) { if (resolution_changed) {
const coordinates = buffer.calculateComponentCoordinates(); const coordinates = buffer.calculateComponentCoordinates();
info_line.label.position(coordinates.start_x, coordinates.y, coordinates.full_visible_length, null); desktop.position(coordinates.x, coordinates.y + 2, coordinates.visible_length);
session.label.position(coordinates.x, coordinates.y + 2, coordinates.visible_length, config.text_in_center);
login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length); login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length);
password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length); password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length);
@@ -454,28 +376,22 @@ pub fn main() !void {
} }
switch (active_input) { switch (active_input) {
.info_line => info_line.label.handle(null, insert_mode), .session => desktop.handle(null, insert_mode),
.session => session.label.handle(null, insert_mode),
.login => login.handle(null, insert_mode) catch { .login => login.handle(null, insert_mode) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); try info_line.setText(lang.err_alloc);
}, },
.password => password.handle(null, insert_mode) catch { .password => password.handle(null, insert_mode) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); try info_line.setText(lang.err_alloc);
}, },
} }
if (config.clock) |clock| draw_clock: { if (config.clock) |clock| draw_clock: {
if (!can_draw_clock) break :draw_clock; var clock_buf: [32:0]u8 = undefined;
const clock_str = interop.timeAsString(&clock_buf, clock) catch {
var clock_buf: [64:0]u8 = undefined;
const clock_str = interop.timeAsString(&clock_buf, clock);
if (clock_str.len == 0) {
// Backport: I've decided not to localize the error message
try info_line.addMessage("clock string too long", config.error_bg, config.error_fg);
can_draw_clock = false;
break :draw_clock; break :draw_clock;
} };
if (clock_str.len == 0) return error.FormattedTimeEmpty;
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), 0);
} }
@@ -486,10 +402,10 @@ pub fn main() !void {
buffer.drawLabel(lang.login, label_x, label_y + 4); buffer.drawLabel(lang.login, label_x, label_y + 4);
buffer.drawLabel(lang.password, label_x, label_y + 6); buffer.drawLabel(lang.password, label_x, label_y + 6);
info_line.label.draw(); info_line.draw(buffer);
if (!config.hide_key_hints) { if (!config.hide_key_hints) {
var length: usize = ly_top_str.len + 1; var length: u64 = 0;
buffer.drawLabel(config.shutdown_key, length, 0); buffer.drawLabel(config.shutdown_key, length, 0);
length += config.shutdown_key.len + 1; length += config.shutdown_key.len + 1;
@@ -511,25 +427,6 @@ pub fn main() !void {
buffer.drawLabel(" ", length - 1, 0); buffer.drawLabel(" ", length - 1, 0);
buffer.drawLabel(lang.sleep, length, 0); buffer.drawLabel(lang.sleep, length, 0);
length += sleep_len + 1;
}
if (config.brightness_down_key) |key| {
buffer.drawLabel(key, length, 0);
length += key.len + 1;
buffer.drawLabel(" ", length - 1, 0);
buffer.drawLabel(lang.brightness_down, length, 0);
length += brightness_down_len + 1;
}
if (config.brightness_up_key) |key| {
buffer.drawLabel(key, length, 0);
length += key.len + 1;
buffer.drawLabel(" ", length - 1, 0);
buffer.drawLabel(lang.brightness_up, length, 0);
length += brightness_up_len + 1;
} }
} }
@@ -542,14 +439,14 @@ 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_access_console_dev) draw_lock_state: { draw_lock_state: {
const lock_state = interop.getLockState(config.console_dev) catch { const lock_state = interop.getLockState(config.console_dev) catch {
try info_line.addMessage(lang.err_console_dev, config.error_bg, config.error_fg); try info_line.setText(lang.err_console_dev);
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);
const lock_state_y: usize = if (config.clock != null) 1 else 0; const lock_state_y: u64 = if (config.clock != null) 1 else 0;
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);
@@ -559,15 +456,15 @@ pub fn main() !void {
} }
} }
session.label.draw(); desktop.draw();
login.draw(); login.draw();
password.draw(); password.drawMasked(config.asterisk);
} else { } else {
std.Thread.sleep(std.time.ns_per_ms * 10); std.time.sleep(std.time.ns_per_ms * 10);
update = buffer.cascade(); update = buffer.cascade();
if (!update) { if (!update) {
std.Thread.sleep(std.time.ns_per_s * 7); std.time.sleep(std.time.ns_per_s * 7);
auth_fails = 0; auth_fails = 0;
} }
} }
@@ -578,25 +475,16 @@ pub fn main() !void {
var timeout: i32 = -1; var timeout: i32 = -1;
// Calculate the maximum timeout based on current animations, or the (big) clock. If there's none, we wait for the event indefinitely instead // Calculate the maximum timeout based on current animations, or the (big) clock. If there's none, we wait for the event indefinitely instead
if (animate and !animation_timed_out) { if (animate) {
timeout = config.min_refresh_delta; timeout = config.min_refresh_delta;
} else if (config.bigclock and config.clock == null) {
// check how long we have been running so we can turn off the animation var tv: std.c.timeval = undefined;
var tv: interop.system_time.timeval = undefined; _ = std.c.gettimeofday(&tv, null);
_ = interop.system_time.gettimeofday(&tv, null);
if (config.animation_timeout_sec > 0 and tv.tv_sec - tv_zero.tv_sec > config.animation_timeout_sec) {
animation_timed_out = true;
animation.deinit();
}
} else if (config.bigclock != .none and config.clock == null) {
var tv: interop.system_time.timeval = undefined;
_ = interop.system_time.gettimeofday(&tv, null);
timeout = @intCast((60 - @rem(tv.tv_sec, 60)) * 1000 - @divTrunc(tv.tv_usec, 1000) + 1); timeout = @intCast((60 - @rem(tv.tv_sec, 60)) * 1000 - @divTrunc(tv.tv_usec, 1000) + 1);
} else if (config.clock != null or auth_fails >= config.auth_fails) { } else if (config.clock != null or auth_fails >= 10) {
var tv: interop.system_time.timeval = undefined; var tv: std.c.timeval = undefined;
_ = interop.system_time.gettimeofday(&tv, null); _ = std.c.gettimeofday(&tv, null);
timeout = @intCast(1000 - @divTrunc(tv.tv_usec, 1000) + 1); timeout = @intCast(1000 - @divTrunc(tv.tv_usec, 1000) + 1);
} }
@@ -625,26 +513,8 @@ pub fn main() !void {
} else if (pressed_key == sleep_key) { } else if (pressed_key == sleep_key) {
if (config.sleep_cmd) |sleep_cmd| { if (config.sleep_cmd) |sleep_cmd| {
var sleep = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", sleep_cmd }, allocator); var sleep = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", sleep_cmd }, allocator);
sleep.stdout_behavior = .Ignore; _ = sleep.spawnAndWait() catch .{};
sleep.stderr_behavior = .Ignore;
handle_sleep_cmd: {
const process_result = sleep.spawnAndWait() catch {
break :handle_sleep_cmd;
};
if (process_result.Exited != 0) {
try info_line.addMessage(lang.err_sleep, config.error_bg, config.error_fg);
}
}
} }
} else if (brightness_down_key != null and pressed_key == brightness_down_key.?) {
adjustBrightness(allocator, config.brightness_down_cmd) catch {
try info_line.addMessage(lang.err_brightness_change, config.error_bg, config.error_fg);
};
} else if (brightness_up_key != null and pressed_key == brightness_up_key.?) {
adjustBrightness(allocator, config.brightness_up_cmd) catch {
try info_line.addMessage(lang.err_brightness_change, config.error_bg, config.error_fg);
};
} }
}, },
termbox.TB_KEY_CTRL_C => run = false, termbox.TB_KEY_CTRL_C => run = false,
@@ -659,15 +529,13 @@ pub fn main() !void {
}, },
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 = switch (active_input) {
.session, .info_line => .info_line, .session, .login => .session,
.login => .session,
.password => .login, .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 = switch (active_input) {
.info_line => .session,
.session => .login, .session => .login,
.login, .password => .password, .login, .password => .password,
}; };
@@ -675,53 +543,31 @@ pub fn main() !void {
}, },
termbox.TB_KEY_TAB => { termbox.TB_KEY_TAB => {
active_input = switch (active_input) { active_input = switch (active_input) {
.info_line => .session,
.session => .login, .session => .login,
.login => .password, .login => .password,
.password => .info_line, .password => .session,
}; };
update = true; update = true;
}, },
termbox.TB_KEY_BACK_TAB => { termbox.TB_KEY_BACK_TAB => {
active_input = switch (active_input) { active_input = switch (active_input) {
.info_line => .password, .session => .password,
.session => .info_line,
.login => .session, .login => .session,
.password => .login, .password => .login,
}; };
update = true; update = true;
}, },
termbox.TB_KEY_ENTER => authenticate: { termbox.TB_KEY_ENTER => {
if (!config.allow_empty_password and password.text.items.len == 0) {
try info_line.addMessage(lang.err_empty_password, config.error_bg, config.error_fg);
InfoLine.clearRendered(allocator, buffer) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
};
info_line.label.draw();
_ = termbox.tb_present();
break :authenticate;
}
try info_line.addMessage(lang.authenticating, config.bg, config.fg);
InfoLine.clearRendered(allocator, buffer) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
};
info_line.label.draw();
_ = termbox.tb_present();
if (config.save) save_last_settings: { if (config.save) save_last_settings: {
var file = std.fs.cwd().createFile(save_path, .{}) catch break :save_last_settings; var file = std.fs.cwd().createFile(save_path, .{}) catch break :save_last_settings;
defer file.close(); defer file.close();
const save_data = Save{ const save_data = Save{
.user = login.text.items, .user = login.text.items,
.session_index = session.label.current, .session_index = desktop.current,
}; };
ini.writeFromStruct(save_data, file.writer(), null, .{}) catch break :save_last_settings; ini.writeFromStruct(save_data, file.writer(), null, true, .{}) catch break :save_last_settings;
// Delete previous save file if it exists
if (migrator.maybe_save_file) |path| std.fs.cwd().deleteFile(path) catch {};
} }
var shared_err = try SharedError.init(); var shared_err = try SharedError.init();
@@ -733,30 +579,15 @@ pub fn main() !void {
const password_text = try allocator.dupeZ(u8, password.text.items); const password_text = try allocator.dupeZ(u8, password.text.items);
defer allocator.free(password_text); defer allocator.free(password_text);
try info_line.setText(lang.authenticating);
InfoLine.clearRendered(allocator, buffer) catch {};
info_line.draw(buffer);
_ = termbox.tb_present();
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]; const current_environment = desktop.environments.items[desktop.current];
const auth_options = auth.AuthOptions{ auth.authenticate(config, current_environment, login_text, password_text) catch |err| {
.tty = config.tty,
.service_name = config.service_name,
.path = config.path,
.session_log = config.session_log,
.xauth_cmd = config.xauth_cmd,
.setup_cmd = config.setup_cmd,
.login_cmd = config.login_cmd,
.x_cmd = config.x_cmd,
.session_pid = session_pid,
};
// Signal action to give up control on the TTY
const tty_control_transfer_act = std.posix.Sigaction{
.handler = .{ .handler = &ttyControlTransferSignalHandler },
.mask = std.posix.empty_sigset,
.flags = 0,
};
std.posix.sigaction(std.posix.SIG.CHLD, &tty_control_transfer_act, null);
auth.authenticate(auth_options, current_environment, login_text, password_text) catch |err| {
shared_err.writeError(err); shared_err.writeError(err);
std.process.exit(1); std.process.exit(1);
}; };
@@ -764,53 +595,37 @@ pub fn main() !void {
} }
_ = std.posix.waitpid(session_pid, 0); _ = std.posix.waitpid(session_pid, 0);
// HACK: It seems like the session process is not exiting immediately after the waitpid call.
// This is a workaround to ensure the session process has exited before re-initializing the TTY.
std.Thread.sleep(std.time.ns_per_s * 1);
session_pid = -1; session_pid = -1;
} }
// Take back control of the TTY
_ = termbox.tb_init();
_ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_TRUECOLOR);
const auth_err = shared_err.readError(); const auth_err = shared_err.readError();
if (auth_err) |err| { if (auth_err) |err| {
auth_fails += 1; auth_fails += 1;
active_input = .password; active_input = .password;
try info_line.addMessage(getAuthErrorMsg(err, lang), config.error_bg, config.error_fg); try info_line.setText(getAuthErrorMsg(err, lang));
if (config.clear_password or err != error.PamAuthError) password.clear(); if (config.clear_password or err != error.PamAuthError) password.clear();
} else { } else {
if (config.logout_cmd) |logout_cmd| {
var logout_process = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", logout_cmd }, allocator);
_ = logout_process.spawnAndWait() catch .{};
}
password.clear(); password.clear();
try info_line.addMessage(lang.logout, config.bg, config.fg); try info_line.setText(lang.logout);
} }
// Clear the TTY because termbox2 doesn't properly do it
const capability = termbox.global.caps[termbox.TB_CAP_CLEAR_SCREEN];
const capability_slice = capability[0..std.mem.len(capability)];
_ = try std.posix.write(termbox.global.ttyfd, capability_slice);
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) _ = termbox.tb_clear(); if (auth_fails < 10) {
_ = termbox.tb_clear();
_ = termbox.tb_present();
}
update = true; update = true;
// Restore the cursor var restore_cursor = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", config.term_restore_cursor_cmd }, allocator);
_ = termbox.tb_set_cursor(0, 0); _ = restore_cursor.spawnAndWait() catch .{};
_ = termbox.tb_present();
}, },
else => { else => {
if (!insert_mode) { if (!insert_mode) {
switch (event.ch) { switch (event.ch) {
'k' => { 'k' => {
active_input = switch (active_input) { active_input = switch (active_input) {
.session, .info_line => .info_line, .session, .login => .session,
.login => .session,
.password => .login, .password => .login,
}; };
update = true; update = true;
@@ -818,7 +633,6 @@ pub fn main() !void {
}, },
'j' => { 'j' => {
active_input = switch (active_input) { active_input = switch (active_input) {
.info_line => .session,
.session => .login, .session => .login,
.login, .password => .password, .login, .password => .password,
}; };
@@ -835,13 +649,12 @@ pub fn main() !void {
} }
switch (active_input) { switch (active_input) {
.info_line => info_line.label.handle(&event, insert_mode), .session => desktop.handle(&event, insert_mode),
.session => session.label.handle(&event, insert_mode),
.login => login.handle(&event, insert_mode) catch { .login => login.handle(&event, insert_mode) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); try info_line.setText(lang.err_alloc);
}, },
.password => password.handle(&event, insert_mode) catch { .password => password.handle(&event, insert_mode) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); try info_line.setText(lang.err_alloc);
}, },
} }
update = true; update = true;
@@ -850,109 +663,17 @@ pub fn main() !void {
} }
} }
fn addOtherEnvironment(session: *Session, lang: Lang, display_server: DisplayServer, exec: ?[]const u8) !void {
const name = switch (display_server) {
.shell => lang.shell,
.xinitrc => lang.xinitrc,
else => unreachable,
};
try session.addEnvironment(.{
.entry_ini = null,
.name = name,
.xdg_session_desktop = null,
.xdg_desktop_names = null,
.cmd = exec orelse "",
.specifier = switch (display_server) {
.wayland => lang.wayland,
.x11 => lang.x11,
else => lang.other,
},
.display_server = display_server,
});
}
fn crawl(session: *Session, lang: Lang, path: []const u8, display_server: DisplayServer) !void {
var iterable_directory = std.fs.openDirAbsolute(path, .{ .iterate = true }) catch return;
defer iterable_directory.close();
var iterator = iterable_directory.iterate();
while (try iterator.next()) |item| {
if (!std.mem.eql(u8, std.fs.path.extension(item.name), ".desktop")) continue;
const entry_path = try std.fmt.allocPrint(session.label.allocator, "{s}/{s}", .{ path, item.name });
defer session.label.allocator.free(entry_path);
var entry_ini = Ini(Entry).init(session.label.allocator);
_ = try entry_ini.readFileToStruct(entry_path, .{
.fieldHandler = null,
.comment_characters = "#",
});
errdefer entry_ini.deinit();
var xdg_session_desktop: []const u8 = undefined;
const maybe_desktop_names = entry_ini.data.@"Desktop Entry".DesktopNames;
if (maybe_desktop_names) |desktop_names| {
xdg_session_desktop = std.mem.sliceTo(desktop_names, ';');
} else {
// if DesktopNames is empty, we'll take the name of the session file
xdg_session_desktop = std.fs.path.stem(item.name);
}
// Prepare the XDG_CURRENT_DESKTOP environment variable here
const entry = entry_ini.data.@"Desktop Entry";
var xdg_desktop_names: ?[:0]const u8 = null;
if (entry.DesktopNames) |desktop_names| {
for (desktop_names) |*c| {
if (c.* == ';') c.* = ':';
}
xdg_desktop_names = desktop_names;
}
const session_desktop = try session.label.allocator.dupeZ(u8, xdg_session_desktop);
errdefer session.label.allocator.free(session_desktop);
try session.addEnvironment(.{
.entry_ini = entry_ini,
.name = entry.Name,
.xdg_session_desktop = session_desktop,
.xdg_desktop_names = xdg_desktop_names,
.cmd = entry.Exec,
.specifier = switch (display_server) {
.wayland => lang.wayland,
.x11 => lang.x11,
else => lang.other,
},
.display_server = display_server,
});
}
}
fn adjustBrightness(allocator: std.mem.Allocator, cmd: []const u8) !void {
var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", cmd }, allocator);
brightness.stdout_behavior = .Ignore;
brightness.stderr_behavior = .Ignore;
handle_brightness_cmd: {
const process_result = brightness.spawnAndWait() catch {
break :handle_brightness_cmd;
};
if (process_result.Exited != 0) {
return error.BrightnessChangeFailed;
}
}
}
fn getAuthErrorMsg(err: anyerror, lang: Lang) []const u8 { fn getAuthErrorMsg(err: anyerror, lang: Lang) []const u8 {
return switch (err) { return switch (err) {
error.GetPasswordNameFailed => lang.err_pwnam, error.GetPasswordNameFailed => lang.err_pwnam,
error.GetEnvListFailed => lang.err_envlist, error.GetEnvListFailed => lang.err_envlist,
error.XauthFailed => lang.err_xauth, error.XauthFailed => lang.err_xauth,
error.McookieFailed => lang.err_mcookie,
error.XcbConnectionFailed => lang.err_xcb_conn, error.XcbConnectionFailed => lang.err_xcb_conn,
error.GroupInitializationFailed => lang.err_user_init, error.GroupInitializationFailed => lang.err_user_init,
error.SetUserGidFailed => lang.err_user_gid, error.SetUserGidFailed => lang.err_user_gid,
error.SetUserUidFailed => lang.err_user_uid, error.SetUserUidFailed => lang.err_user_uid,
error.ChangeDirectoryFailed => lang.err_perm_dir, error.ChangeDirectoryFailed => lang.err_perm_dir,
error.TtyControlTransferFailed => lang.err_tty_ctrl,
error.SetPathFailed => lang.err_path, error.SetPathFailed => lang.err_path,
error.PamAccountExpired => lang.err_pam_acct_expired, error.PamAccountExpired => lang.err_pam_acct_expired,
error.PamAuthError => lang.err_pam_auth, error.PamAuthError => lang.err_pam_auth,

View File

@@ -1,61 +0,0 @@
const Animation = @This();
const VTable = struct {
deinit_fn: *const fn (ptr: *anyopaque) void,
realloc_fn: *const fn (ptr: *anyopaque) anyerror!void,
draw_fn: *const fn (ptr: *anyopaque) void,
};
pointer: *anyopaque,
vtable: VTable,
pub fn init(
pointer: anytype,
comptime deinit_fn: fn (ptr: @TypeOf(pointer)) void,
comptime realloc_fn: fn (ptr: @TypeOf(pointer)) anyerror!void,
comptime draw_fn: fn (ptr: @TypeOf(pointer)) void,
) Animation {
const Pointer = @TypeOf(pointer);
const Impl = struct {
pub fn deinitImpl(ptr: *anyopaque) void {
const impl: Pointer = @ptrCast(@alignCast(ptr));
return @call(.always_inline, deinit_fn, .{impl});
}
pub fn reallocImpl(ptr: *anyopaque) anyerror!void {
const impl: Pointer = @ptrCast(@alignCast(ptr));
return @call(.always_inline, realloc_fn, .{impl});
}
pub fn drawImpl(ptr: *anyopaque) void {
const impl: Pointer = @ptrCast(@alignCast(ptr));
return @call(.always_inline, draw_fn, .{impl});
}
const vtable = VTable{
.deinit_fn = deinitImpl,
.realloc_fn = reallocImpl,
.draw_fn = drawImpl,
};
};
return .{
.pointer = pointer,
.vtable = Impl.vtable,
};
}
pub fn deinit(self: *Animation) void {
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
return @call(.auto, self.vtable.deinit_fn, .{impl});
}
pub fn realloc(self: *Animation) anyerror!void {
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
return @call(.auto, self.vtable.realloc_fn, .{impl});
}
pub fn draw(self: *Animation) void {
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
return @call(.auto, self.vtable.draw_fn, .{impl});
}

View File

@@ -1,23 +0,0 @@
const interop = @import("../interop.zig");
const termbox = interop.termbox;
const Cell = @This();
ch: u32,
fg: u32,
bg: u32,
pub fn init(ch: u32, fg: u32, bg: u32) Cell {
return .{
.ch = ch,
.fg = fg,
.bg = bg,
};
}
pub fn put(self: Cell, x: usize, y: usize) void {
if (self.ch == 0) return;
_ = termbox.tb_set_cell(@intCast(x), @intCast(y), self.ch, self.fg, self.bg);
}

View File

@@ -1,7 +1,8 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const interop = @import("../interop.zig"); const interop = @import("../interop.zig");
const Cell = @import("Cell.zig"); const utils = @import("utils.zig");
const Config = @import("../config/Config.zig");
const Random = std.Random; const Random = std.Random;
@@ -9,44 +10,13 @@ const termbox = interop.termbox;
const TerminalBuffer = @This(); const TerminalBuffer = @This();
pub const InitOptions = struct {
fg: u32,
bg: u32,
border_fg: u32,
margin_box_h: u8,
margin_box_v: u8,
input_len: u8,
};
pub const Styling = struct {
pub const BOLD = termbox.TB_BOLD;
pub const UNDERLINE = termbox.TB_UNDERLINE;
pub const REVERSE = termbox.TB_REVERSE;
pub const ITALIC = termbox.TB_ITALIC;
pub const BLINK = termbox.TB_BLINK;
pub const HI_BLACK = termbox.TB_HI_BLACK;
pub const BRIGHT = termbox.TB_BRIGHT;
pub const DIM = termbox.TB_DIM;
};
pub const Color = struct {
pub const DEFAULT = 0x00000000;
pub const BLACK = Styling.HI_BLACK;
pub const RED = 0x00FF0000;
pub const GREEN = 0x0000FF00;
pub const YELLOW = 0x00FFFF00;
pub const BLUE = 0x000000FF;
pub const MAGENTA = 0x00FF00FF;
pub const CYAN = 0x0000FFFF;
pub const WHITE = 0x00FFFFFF;
};
random: Random, random: Random,
width: usize, width: u64,
height: usize, height: u64,
fg: u32, buffer: [*]termbox.tb_cell,
bg: u32, fg: u8,
border_fg: u32, bg: u8,
border_fg: u8,
box_chars: struct { box_chars: struct {
left_up: u32, left_up: u32,
left_down: u32, left_down: u32,
@@ -57,23 +27,23 @@ box_chars: struct {
left: u32, left: u32,
right: u32, right: u32,
}, },
labels_max_length: usize, labels_max_length: u64,
box_x: usize, box_x: u64,
box_y: usize, box_y: u64,
box_width: usize, box_width: u64,
box_height: usize, box_height: u64,
margin_box_v: u8, margin_box_v: u8,
margin_box_h: u8, margin_box_h: u8,
blank_cell: Cell,
pub fn init(options: InitOptions, labels_max_length: usize, random: Random) TerminalBuffer { pub fn init(config: Config, labels_max_length: u64, random: Random) TerminalBuffer {
return .{ return .{
.random = random, .random = random,
.width = @intCast(termbox.tb_width()), .width = @intCast(termbox.tb_width()),
.height = @intCast(termbox.tb_height()), .height = @intCast(termbox.tb_height()),
.fg = options.fg, .buffer = termbox.tb_cell_buffer(),
.bg = options.bg, .fg = config.fg,
.border_fg = options.border_fg, .bg = config.bg,
.border_fg = config.border_fg,
.box_chars = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) .{ .box_chars = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) .{
.left_up = 0x250C, .left_up = 0x250C,
.left_down = 0x2514, .left_down = 0x2514,
@@ -96,42 +66,35 @@ pub fn init(options: InitOptions, labels_max_length: usize, random: Random) Term
.labels_max_length = labels_max_length, .labels_max_length = labels_max_length,
.box_x = 0, .box_x = 0,
.box_y = 0, .box_y = 0,
.box_width = (2 * options.margin_box_h) + options.input_len + 1 + labels_max_length, .box_width = (2 * config.margin_box_h) + config.input_len + 1 + labels_max_length,
.box_height = 7 + (2 * options.margin_box_v), .box_height = 7 + (2 * config.margin_box_v),
.margin_box_v = options.margin_box_v, .margin_box_v = config.margin_box_v,
.margin_box_h = options.margin_box_h, .margin_box_h = config.margin_box_h,
.blank_cell = Cell.init(' ', options.fg, options.bg),
}; };
} }
pub fn cascade(self: TerminalBuffer) bool { pub fn cascade(self: TerminalBuffer) bool {
var changed = false; var changes = false;
var y = self.height - 2;
var y = self.height - 2;
while (y > 0) : (y -= 1) { while (y > 0) : (y -= 1) {
for (0..self.width) |x| { for (0..self.width) |x| {
var cell: termbox.tb_cell = undefined; const c: u8 = @truncate(self.buffer[(y - 1) * self.width + x].ch);
var cell_under: termbox.tb_cell = undefined; if (std.ascii.isWhitespace(c)) continue;
_ = termbox.tb_get_cell(@intCast(x), @intCast(y - 1), 1, &cell); const c_under: u8 = @truncate(self.buffer[y * self.width + x].ch);
_ = termbox.tb_get_cell(@intCast(x), @intCast(y), 1, &cell_under); if (!std.ascii.isWhitespace(c_under)) continue;
const char: u8 = @truncate(cell.ch); changes = true;
if (std.ascii.isWhitespace(char)) continue;
const char_under: u8 = @truncate(cell_under.ch);
if (!std.ascii.isWhitespace(char_under)) continue;
changed = true;
if ((self.random.int(u16) % 10) > 7) continue; if ((self.random.int(u16) % 10) > 7) continue;
_ = termbox.tb_set_cell(@intCast(x), @intCast(y), cell.ch, cell.fg, cell.bg); self.buffer[y * self.width + x] = self.buffer[(y - 1) * self.width + x];
_ = termbox.tb_set_cell(@intCast(x), @intCast(y - 1), ' ', cell_under.fg, cell_under.bg); self.buffer[(y - 1) * self.width + x].ch = ' ';
} }
} }
return changed; return changes;
} }
pub fn drawBoxCenter(self: *TerminalBuffer, show_borders: bool, blank_box: bool) void { pub fn drawBoxCenter(self: *TerminalBuffer, show_borders: bool, blank_box: bool) void {
@@ -150,92 +113,76 @@ pub fn drawBoxCenter(self: *TerminalBuffer, show_borders: bool, blank_box: bool)
_ = termbox.tb_set_cell(@intCast(x1 - 1), @intCast(y2), self.box_chars.left_down, self.border_fg, self.bg); _ = termbox.tb_set_cell(@intCast(x1 - 1), @intCast(y2), self.box_chars.left_down, self.border_fg, self.bg);
_ = termbox.tb_set_cell(@intCast(x2), @intCast(y2), self.box_chars.right_down, self.border_fg, self.bg); _ = termbox.tb_set_cell(@intCast(x2), @intCast(y2), self.box_chars.right_down, self.border_fg, self.bg);
var c1 = Cell.init(self.box_chars.top, self.border_fg, self.bg); var c1 = utils.initCell(self.box_chars.top, self.border_fg, self.bg);
var c2 = Cell.init(self.box_chars.bottom, self.border_fg, self.bg); var c2 = utils.initCell(self.box_chars.bottom, self.border_fg, self.bg);
for (0..self.box_width) |i| { for (0..self.box_width) |i| {
c1.put(x1 + i, y1 - 1); _ = utils.putCell(@intCast(x1 + i), @intCast(y1 - 1), &c1);
c2.put(x1 + i, y2); _ = utils.putCell(@intCast(x1 + i), @intCast(y2), &c2);
} }
c1.ch = self.box_chars.left; c1.ch = self.box_chars.left;
c2.ch = self.box_chars.right; c2.ch = self.box_chars.right;
for (0..self.box_height) |i| { for (0..self.box_height) |i| {
c1.put(x1 - 1, y1 + i); _ = utils.putCell(@intCast(x1 - 1), @intCast(y1 + i), &c1);
c2.put(x2, y1 + i); _ = utils.putCell(@intCast(x2), @intCast(y1 + i), &c2);
} }
} }
if (blank_box) { if (blank_box) {
const blank = utils.initCell(' ', self.fg, self.bg);
for (0..self.box_height) |y| { for (0..self.box_height) |y| {
for (0..self.box_width) |x| { for (0..self.box_width) |x| {
self.blank_cell.put(x1 + x, y1 + y); _ = utils.putCell(@intCast(x1 + x), @intCast(y1 + y), &blank);
} }
} }
} }
} }
pub fn calculateComponentCoordinates(self: TerminalBuffer) struct { pub fn calculateComponentCoordinates(self: TerminalBuffer) struct {
start_x: usize, x: u64,
x: usize, y: u64,
y: usize, visible_length: u64,
full_visible_length: usize,
visible_length: usize,
} { } {
const start_x = self.box_x + self.margin_box_h; const x = self.box_x + self.margin_box_h + self.labels_max_length + 1;
const x = start_x + self.labels_max_length + 1;
const y = self.box_y + self.margin_box_v; const y = self.box_y + self.margin_box_v;
const full_visible_length = self.box_x + self.box_width - self.margin_box_h - start_x;
const visible_length = self.box_x + self.box_width - self.margin_box_h - x; const visible_length = self.box_x + self.box_width - self.margin_box_h - x;
return .{ return .{
.start_x = start_x,
.x = x, .x = x,
.y = y, .y = y,
.full_visible_length = full_visible_length,
.visible_length = visible_length, .visible_length = visible_length,
}; };
} }
pub fn drawLabel(self: TerminalBuffer, text: []const u8, x: usize, y: usize) void { pub fn drawLabel(self: TerminalBuffer, text: []const u8, x: u64, y: u64) void {
drawColorLabel(text, x, y, self.fg, self.bg);
}
pub fn drawColorLabel(text: []const u8, x: usize, y: usize, fg: u32, bg: u32) void {
const yc: c_int = @intCast(y); const yc: c_int = @intCast(y);
const utf8view = std.unicode.Utf8View.init(text) catch return; const utf8view = std.unicode.Utf8View.init(text) catch return;
var utf8 = utf8view.iterator(); var utf8 = utf8view.iterator();
var i: c_int = @intCast(x); var i = x;
while (utf8.nextCodepoint()) |codepoint| : (i += termbox.tb_wcwidth(codepoint)) { while (utf8.nextCodepoint()) |codepoint| : (i += 1) {
_ = termbox.tb_set_cell(i, yc, codepoint, fg, bg); _ = termbox.tb_set_cell(@intCast(i), yc, codepoint, self.fg, self.bg);
} }
} }
pub fn drawConfinedLabel(self: TerminalBuffer, text: []const u8, x: usize, y: usize, max_length: usize) void { pub fn drawConfinedLabel(self: TerminalBuffer, text: []const u8, x: u64, y: u64, max_length: u64) void {
const yc: c_int = @intCast(y); const yc: c_int = @intCast(y);
const utf8view = std.unicode.Utf8View.init(text) catch return; const utf8view = std.unicode.Utf8View.init(text) catch return;
var utf8 = utf8view.iterator(); var utf8 = utf8view.iterator();
var i: c_int = @intCast(x); var i: usize = 0;
while (utf8.nextCodepoint()) |codepoint| : (i += termbox.tb_wcwidth(codepoint)) { while (utf8.nextCodepoint()) |codepoint| : (i += 1) {
if (i >= max_length) break; if (i >= max_length) break;
_ = termbox.tb_set_cell(i, yc, codepoint, self.fg, self.bg); _ = termbox.tb_set_cell(@intCast(i + x), yc, codepoint, self.fg, self.bg);
} }
} }
pub fn drawCharMultiple(self: TerminalBuffer, char: u32, x: usize, y: usize, length: usize) void { pub fn drawCharMultiple(self: TerminalBuffer, char: u8, x: u64, y: u64, length: u64) void {
const cell = Cell.init(char, self.fg, self.bg); const yc: c_int = @intCast(y);
for (0..length) |xx| cell.put(x + xx, y); const cell = utils.initCell(char, self.fg, self.bg);
}
// Every codepoint is assumed to have a width of 1. for (0..length) |xx| _ = utils.putCell(@intCast(x + xx), yc, &cell);
// Since Ly is normally running in a TTY, this should be fine.
pub fn strWidth(str: []const u8) !u8 {
const utf8view = try std.unicode.Utf8View.init(str);
var utf8 = utf8view.iterator();
var i: c_int = 0;
while (utf8.nextCodepoint()) |codepoint| i += termbox.tb_wcwidth(codepoint);
return @intCast(i);
} }

View File

@@ -0,0 +1,223 @@
const std = @import("std");
const enums = @import("../../enums.zig");
const interop = @import("../../interop.zig");
const TerminalBuffer = @import("../TerminalBuffer.zig");
const Ini = @import("zigini").Ini;
const Lang = @import("../../config/Lang.zig");
const Allocator = std.mem.Allocator;
const EnvironmentList = std.ArrayList(Environment);
const DisplayServer = enums.DisplayServer;
const termbox = interop.termbox;
const Desktop = @This();
pub const Environment = struct {
entry_ini: ?Ini(Entry) = null,
name: [:0]const u8 = "",
xdg_session_desktop: [:0]const u8 = "",
xdg_desktop_names: ?[:0]const u8 = "",
cmd: []const u8 = "",
specifier: []const u8 = "",
display_server: DisplayServer = .wayland,
};
const DesktopEntry = struct {
Exec: []const u8 = "",
Name: [:0]const u8 = "",
DesktopNames: ?[]const u8 = null,
};
pub const Entry = struct { @"Desktop Entry": DesktopEntry = DesktopEntry{} };
allocator: Allocator,
buffer: *TerminalBuffer,
environments: EnvironmentList,
current: u64,
visible_length: u64,
x: u64,
y: u64,
lang: Lang,
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: u64, lang: Lang) !Desktop {
return .{
.allocator = allocator,
.buffer = buffer,
.environments = try EnvironmentList.initCapacity(allocator, max_length),
.current = 0,
.visible_length = 0,
.x = 0,
.y = 0,
.lang = lang,
};
}
pub fn deinit(self: Desktop) void {
for (self.environments.items) |*environment| {
if (environment.entry_ini) |*entry_ini| entry_ini.deinit();
if (environment.xdg_desktop_names) |desktop_name| self.allocator.free(desktop_name);
self.allocator.free(environment.xdg_session_desktop);
}
self.environments.deinit();
}
pub fn position(self: *Desktop, x: u64, y: u64, visible_length: u64) void {
self.x = x;
self.y = y;
self.visible_length = visible_length;
}
pub fn addEnvironment(self: *Desktop, entry: DesktopEntry, xdg_session_desktop: []const u8, display_server: DisplayServer) !void {
var xdg_desktop_names: ?[:0]const u8 = null;
if (entry.DesktopNames) |desktop_names| {
const desktop_names_z = try self.allocator.dupeZ(u8, desktop_names);
for (desktop_names_z) |*c| {
if (c.* == ';') c.* = ':';
}
xdg_desktop_names = desktop_names_z;
}
errdefer {
if (xdg_desktop_names) |desktop_names| self.allocator.free(desktop_names);
}
const session_desktop = try self.allocator.dupeZ(u8, xdg_session_desktop);
errdefer self.allocator.free(session_desktop);
try self.environments.append(.{
.entry_ini = null,
.name = entry.Name,
.xdg_session_desktop = session_desktop,
.xdg_desktop_names = xdg_desktop_names,
.cmd = entry.Exec,
.specifier = switch (display_server) {
.wayland => self.lang.wayland,
.x11 => self.lang.x11,
else => self.lang.other,
},
.display_server = display_server,
});
self.current = self.environments.items.len - 1;
}
pub fn addEnvironmentWithIni(self: *Desktop, entry_ini: Ini(Entry), xdg_session_desktop: []const u8, display_server: DisplayServer) !void {
const entry = entry_ini.data.@"Desktop Entry";
var xdg_desktop_names: ?[:0]const u8 = null;
if (entry.DesktopNames) |desktop_names| {
const desktop_names_z = try self.allocator.dupeZ(u8, desktop_names);
for (desktop_names_z) |*c| {
if (c.* == ';') c.* = ':';
}
xdg_desktop_names = desktop_names_z;
}
errdefer {
if (xdg_desktop_names) |desktop_names| self.allocator.free(desktop_names);
}
const session_desktop = try self.allocator.dupeZ(u8, xdg_session_desktop);
errdefer self.allocator.free(session_desktop);
try self.environments.append(.{
.entry_ini = entry_ini,
.name = entry.Name,
.xdg_session_desktop = session_desktop,
.xdg_desktop_names = xdg_desktop_names,
.cmd = entry.Exec,
.specifier = switch (display_server) {
.wayland => self.lang.wayland,
.x11 => self.lang.x11,
else => self.lang.other,
},
.display_server = display_server,
});
self.current = self.environments.items.len - 1;
}
pub fn crawl(self: *Desktop, path: []const u8, display_server: DisplayServer) !void {
var iterable_directory = std.fs.openDirAbsolute(path, .{ .iterate = true }) catch return;
defer iterable_directory.close();
var iterator = iterable_directory.iterate();
while (try iterator.next()) |item| {
if (!std.mem.eql(u8, std.fs.path.extension(item.name), ".desktop")) continue;
const entry_path = try std.fmt.allocPrint(self.allocator, "{s}/{s}", .{ path, item.name });
defer self.allocator.free(entry_path);
var entry_ini = Ini(Entry).init(self.allocator);
_ = try entry_ini.readFileToStruct(entry_path, "#", null);
errdefer entry_ini.deinit();
var xdg_session_desktop: []const u8 = undefined;
const maybe_desktop_names = entry_ini.data.@"Desktop Entry".DesktopNames;
if (maybe_desktop_names) |desktop_names| {
xdg_session_desktop = std.mem.sliceTo(desktop_names, ';');
} else {
// if DesktopNames is empty, we'll take the name of the session file
xdg_session_desktop = std.fs.path.stem(item.name);
}
try self.addEnvironmentWithIni(entry_ini, xdg_session_desktop, display_server);
}
}
pub fn handle(self: *Desktop, maybe_event: ?*termbox.tb_event, insert_mode: bool) void {
if (maybe_event) |event| blk: {
if (event.type != termbox.TB_EVENT_KEY) break :blk;
switch (event.key) {
termbox.TB_KEY_ARROW_LEFT, termbox.TB_KEY_CTRL_H => self.goLeft(),
termbox.TB_KEY_ARROW_RIGHT, termbox.TB_KEY_CTRL_L => self.goRight(),
else => {
if (!insert_mode) {
switch (event.ch) {
'h' => self.goLeft(),
'l' => self.goRight(),
else => {},
}
}
},
}
}
_ = termbox.tb_set_cursor(@intCast(self.x + 2), @intCast(self.y));
}
pub fn draw(self: Desktop) void {
const environment = self.environments.items[self.current];
const length = @min(environment.name.len, self.visible_length - 3);
if (length == 0) return;
const x = self.buffer.box_x + self.buffer.margin_box_h;
const y = self.buffer.box_y + self.buffer.margin_box_v + 2;
self.buffer.drawLabel(environment.specifier, x, y);
_ = termbox.tb_set_cell(@intCast(self.x), @intCast(self.y), '<', self.buffer.fg, self.buffer.bg);
_ = termbox.tb_set_cell(@intCast(self.x + self.visible_length - 1), @intCast(self.y), '>', self.buffer.fg, self.buffer.bg);
self.buffer.drawLabel(environment.name, self.x + 2, self.y);
}
fn goLeft(self: *Desktop) void {
if (self.current == 0) {
self.current = self.environments.items.len - 1;
return;
}
self.current -= 1;
}
fn goRight(self: *Desktop) void {
if (self.current == self.environments.items.len - 1) {
self.current = 0;
return;
}
self.current += 1;
}

View File

@@ -1,45 +1,28 @@
const std = @import("std"); const std = @import("std");
const utils = @import("../utils.zig");
const TerminalBuffer = @import("../TerminalBuffer.zig"); const TerminalBuffer = @import("../TerminalBuffer.zig");
const generic = @import("generic.zig");
const Allocator = std.mem.Allocator;
const MessageLabel = generic.CyclableLabel(Message);
const InfoLine = @This(); const InfoLine = @This();
const Message = struct { text: []const u8 = "",
width: u8, width: u8 = 0,
text: []const u8,
bg: u32,
fg: u32,
};
label: MessageLabel, pub fn setText(self: *InfoLine, text: []const u8) !void {
self.width = if (text.len > 0) try utils.strWidth(text) else 0;
pub fn init(allocator: Allocator, buffer: *TerminalBuffer) InfoLine { self.text = text;
return .{
.label = MessageLabel.init(allocator, buffer, drawItem),
};
} }
pub fn deinit(self: *InfoLine) void { pub fn draw(self: InfoLine, buffer: TerminalBuffer) void {
self.label.deinit(); if (self.width > 0 and buffer.box_width > self.width) {
const label_y = buffer.box_y + buffer.margin_box_v;
const x = buffer.box_x + ((buffer.box_width - self.width) / 2);
buffer.drawLabel(self.text, x, label_y);
}
} }
pub fn addMessage(self: *InfoLine, text: []const u8, bg: u32, fg: u32) !void { pub fn clearRendered(allocator: std.mem.Allocator, buffer: TerminalBuffer) !void {
if (text.len == 0) return; // draw over the area
try self.label.addItem(.{
.width = try TerminalBuffer.strWidth(text),
.text = text,
.bg = bg,
.fg = fg,
});
}
pub fn clearRendered(allocator: Allocator, buffer: TerminalBuffer) !void {
// Draw over the area
const y = buffer.box_y + buffer.margin_box_v; const y = buffer.box_y + buffer.margin_box_v;
const spaces = try allocator.alloc(u8, buffer.box_width); const spaces = try allocator.alloc(u8, buffer.box_width);
defer allocator.free(spaces); defer allocator.free(spaces);
@@ -48,13 +31,3 @@ pub fn clearRendered(allocator: Allocator, buffer: TerminalBuffer) !void {
buffer.drawLabel(spaces, buffer.box_x, y); buffer.drawLabel(spaces, buffer.box_x, y);
} }
fn drawItem(label: *MessageLabel, message: Message, _: usize, _: usize) bool {
if (message.width == 0 or label.buffer.box_width <= message.width) return false;
const x = label.buffer.box_x + ((label.buffer.box_width - message.width) / 2);
label.first_char_x = x + message.width;
TerminalBuffer.drawColorLabel(message.text, x, label.y, message.fg, message.bg);
return true;
}

View File

@@ -1,46 +0,0 @@
const std = @import("std");
const TerminalBuffer = @import("../TerminalBuffer.zig");
const enums = @import("../../enums.zig");
const ini = @import("zigini");
const Environment = @import("../../Environment.zig");
const generic = @import("generic.zig");
const Allocator = std.mem.Allocator;
const DisplayServer = enums.DisplayServer;
const Ini = ini.Ini;
const EnvironmentLabel = generic.CyclableLabel(Environment);
const Session = @This();
label: EnvironmentLabel,
pub fn init(allocator: Allocator, buffer: *TerminalBuffer) Session {
return .{
.label = EnvironmentLabel.init(allocator, buffer, drawItem),
};
}
pub fn deinit(self: *Session) void {
for (self.label.list.items) |*environment| {
if (environment.entry_ini) |*entry_ini| entry_ini.deinit();
if (environment.xdg_session_desktop) |session_desktop| self.label.allocator.free(session_desktop);
}
self.label.deinit();
}
pub fn addEnvironment(self: *Session, environment: Environment) !void {
try self.label.addItem(environment);
}
fn drawItem(label: *EnvironmentLabel, environment: Environment, x: usize, y: usize) bool {
const length = @min(environment.name.len, label.visible_length - 3);
if (length == 0) return false;
const nx = if (label.text_in_center) (label.x + (label.visible_length - environment.name.len) / 2) else (label.x + 2);
label.first_char_x = nx + environment.name.len;
label.buffer.drawLabel(environment.specifier, x, y);
label.buffer.drawLabel(environment.name, nx, label.y);
return true;
}

View File

@@ -1,9 +1,10 @@
const std = @import("std"); const std = @import("std");
const interop = @import("../../interop.zig"); const interop = @import("../../interop.zig");
const TerminalBuffer = @import("../TerminalBuffer.zig"); const TerminalBuffer = @import("../TerminalBuffer.zig");
const utils = @import("../utils.zig");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const DynamicString = std.ArrayListUnmanaged(u8); const DynamicString = std.ArrayList(u8);
const termbox = interop.termbox; const termbox = interop.termbox;
@@ -12,17 +13,15 @@ const Text = @This();
allocator: Allocator, allocator: Allocator,
buffer: *TerminalBuffer, buffer: *TerminalBuffer,
text: DynamicString, text: DynamicString,
end: usize, end: u64,
cursor: usize, cursor: u64,
visible_start: usize, visible_start: u64,
visible_length: usize, visible_length: u64,
x: usize, x: u64,
y: usize, y: u64,
masked: bool,
maybe_mask: ?u32,
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, masked: bool, maybe_mask: ?u32) Text { pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: u64) !Text {
const text: DynamicString = .empty; const text = try DynamicString.initCapacity(allocator, max_length);
return .{ return .{
.allocator = allocator, .allocator = allocator,
@@ -34,16 +33,14 @@ pub fn init(allocator: Allocator, buffer: *TerminalBuffer, masked: bool, maybe_m
.visible_length = 0, .visible_length = 0,
.x = 0, .x = 0,
.y = 0, .y = 0,
.masked = masked,
.maybe_mask = maybe_mask,
}; };
} }
pub fn deinit(self: *Text) void { pub fn deinit(self: Text) void {
self.text.deinit(self.allocator); self.text.deinit();
} }
pub fn position(self: *Text, x: usize, y: usize, visible_length: usize) void { pub fn position(self: *Text, x: u64, y: u64, visible_length: u64) void {
self.x = x; self.x = x;
self.y = y; self.y = y;
self.visible_length = visible_length; self.visible_length = visible_length;
@@ -81,25 +78,10 @@ pub fn handle(self: *Text, maybe_event: ?*termbox.tb_event, insert_mode: bool) !
} }
} }
if (self.masked and self.maybe_mask == null) {
_ = termbox.tb_set_cursor(@intCast(self.x), @intCast(self.y));
return;
}
_ = termbox.tb_set_cursor(@intCast(self.x + (self.cursor - self.visible_start)), @intCast(self.y)); _ = termbox.tb_set_cursor(@intCast(self.x + (self.cursor - self.visible_start)), @intCast(self.y));
} }
pub fn draw(self: Text) void { pub fn draw(self: Text) void {
if (self.masked) {
if (self.maybe_mask) |mask| {
const length = @min(self.text.items.len, self.visible_length - 1);
if (length == 0) return;
self.buffer.drawCharMultiple(mask, self.x, self.y, length);
}
return;
}
const length = @min(self.text.items.len, self.visible_length); const length = @min(self.text.items.len, self.visible_length);
if (length == 0) return; if (length == 0) return;
@@ -114,6 +96,13 @@ pub fn draw(self: Text) void {
self.buffer.drawLabel(visible_slice, self.x, self.y); self.buffer.drawLabel(visible_slice, self.x, self.y);
} }
pub fn drawMasked(self: Text, mask: u8) void {
const length = @min(self.text.items.len, self.visible_length - 1);
if (length == 0) return;
self.buffer.drawCharMultiple(mask, self.x, self.y, length);
}
pub fn clear(self: *Text) void { pub fn clear(self: *Text) void {
self.text.clearRetainingCapacity(); self.text.clearRetainingCapacity();
self.end = 0; self.end = 0;
@@ -153,7 +142,7 @@ fn backspace(self: *Text) void {
fn write(self: *Text, char: u8) !void { fn write(self: *Text, char: u8) !void {
if (char == 0) return; if (char == 0) return;
try self.text.insert(self.allocator, self.cursor, char); try self.text.insert(self.cursor, char);
self.end += 1; self.end += 1;
self.goRight(); self.goRight();

View File

@@ -1,114 +0,0 @@
const std = @import("std");
const interop = @import("../../interop.zig");
const TerminalBuffer = @import("../TerminalBuffer.zig");
pub fn CyclableLabel(comptime ItemType: type) type {
return struct {
const Allocator = std.mem.Allocator;
const ItemList = std.ArrayListUnmanaged(ItemType);
const DrawItemFn = *const fn (*Self, ItemType, usize, usize) bool;
const termbox = interop.termbox;
const Self = @This();
allocator: Allocator,
buffer: *TerminalBuffer,
list: ItemList,
current: usize,
visible_length: usize,
x: usize,
y: usize,
first_char_x: usize,
text_in_center: bool,
draw_item_fn: DrawItemFn,
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, draw_item_fn: DrawItemFn) Self {
return .{
.allocator = allocator,
.buffer = buffer,
.list = .empty,
.current = 0,
.visible_length = 0,
.x = 0,
.y = 0,
.first_char_x = 0,
.text_in_center = false,
.draw_item_fn = draw_item_fn,
};
}
pub fn deinit(self: *Self) void {
self.list.deinit(self.allocator);
}
pub fn position(self: *Self, x: usize, y: usize, visible_length: usize, text_in_center: ?bool) void {
self.x = x;
self.y = y;
self.visible_length = visible_length;
self.first_char_x = x + 2;
if (text_in_center) |value| {
self.text_in_center = value;
}
}
pub fn addItem(self: *Self, item: ItemType) !void {
try self.list.append(self.allocator, item);
self.current = self.list.items.len - 1;
}
pub fn handle(self: *Self, maybe_event: ?*termbox.tb_event, insert_mode: bool) void {
if (maybe_event) |event| blk: {
if (event.type != termbox.TB_EVENT_KEY) break :blk;
switch (event.key) {
termbox.TB_KEY_ARROW_LEFT, termbox.TB_KEY_CTRL_H => self.goLeft(),
termbox.TB_KEY_ARROW_RIGHT, termbox.TB_KEY_CTRL_L => self.goRight(),
else => {
if (!insert_mode) {
switch (event.ch) {
'h' => self.goLeft(),
'l' => self.goRight(),
else => {},
}
}
},
}
}
_ = termbox.tb_set_cursor(@intCast(self.first_char_x), @intCast(self.y));
}
pub fn draw(self: *Self) void {
if (self.list.items.len == 0) return;
const current_item = self.list.items[self.current];
const x = self.buffer.box_x + self.buffer.margin_box_h;
const y = self.buffer.box_y + self.buffer.margin_box_v + 2;
const continue_drawing = @call(.auto, self.draw_item_fn, .{ self, current_item, x, y });
if (!continue_drawing) return;
_ = termbox.tb_set_cell(@intCast(self.x), @intCast(self.y), '<', self.buffer.fg, self.buffer.bg);
_ = termbox.tb_set_cell(@intCast(self.x + self.visible_length - 1), @intCast(self.y), '>', self.buffer.fg, self.buffer.bg);
}
fn goLeft(self: *Self) void {
if (self.current == 0) {
self.current = self.list.items.len - 1;
return;
}
self.current -= 1;
}
fn goRight(self: *Self) void {
if (self.current == self.list.items.len - 1) {
self.current = 0;
return;
}
self.current += 1;
}
};
}

26
src/tui/utils.zig Normal file
View File

@@ -0,0 +1,26 @@
const std = @import("std");
const interop = @import("../interop.zig");
const termbox = interop.termbox;
pub fn initCell(ch: u32, fg: u16, bg: u16) termbox.tb_cell {
return .{
.ch = ch,
.fg = fg,
.bg = bg,
};
}
pub fn putCell(x: i32, y: i32, cell: *const termbox.tb_cell) c_int {
return termbox.tb_set_cell(x, y, cell.ch, cell.fg, cell.bg);
}
// Every codepoint is assumed to have a width of 1.
// Since ly should be running in a tty, this should be fine.
pub fn strWidth(str: []const u8) !u8 {
const utf8view = try std.unicode.Utf8View.init(str);
var utf8 = utf8view.iterator();
var i: u8 = 0;
while (utf8.nextCodepoint()) |_| i += 1;
return i;
}