mirror of
https://github.com/fairyglade/ly.git
synced 2025-12-21 11:44:55 +00:00
Compare commits
26 Commits
v1.1.2
...
dhalucario
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
271b4f8898 | ||
|
|
552d533435 | ||
|
|
9d774d93c5 | ||
|
|
90f18e9b34 | ||
|
|
ccb9dfabc5 | ||
|
|
4ba42400ce | ||
|
|
b336b70605 | ||
|
|
9c70ff5576 | ||
|
|
ec230541f3 | ||
|
|
d2fcb2e87d | ||
|
|
96a556a345 | ||
|
|
f79330cda3 | ||
|
|
3deedba040 | ||
|
|
dfe918358e | ||
|
|
cd738eafa7 | ||
|
|
289624bc88 | ||
|
|
7d4b25fc70 | ||
|
|
b8b31386e1 | ||
|
|
a14e6b5224 | ||
|
|
16922531e2 | ||
|
|
5c04c996d5 | ||
|
|
a7dedbab1b | ||
|
|
1926901eda | ||
|
|
43a40faf79 | ||
|
|
974aca51cb | ||
|
|
6ac03ab27e |
4
.copr/Makefile
Normal file
4
.copr/Makefile
Normal file
@@ -0,0 +1,4 @@
|
||||
srpm:
|
||||
cd $(spec)
|
||||
make github
|
||||
rpmbuild -vv -bs ly.spec --define "_srcrpmdir $(outdir)"
|
||||
15
.gitea
Normal file
15
.gitea
Normal file
@@ -0,0 +1,15 @@
|
||||
[submodule "sub/argoat"]
|
||||
path = sub/argoat
|
||||
url = https://git.nullgemm.fr/nullgemm/argoat.git
|
||||
[submodule "sub/configator"]
|
||||
path = sub/configator
|
||||
url = https://git.nullgemm.fr/nullgemm/configator.git
|
||||
[submodule "sub/ctypes"]
|
||||
path = sub/ctypes
|
||||
url = https://git.nullgemm.fr/nullgemm/ctypes.git
|
||||
[submodule "sub/dragonfail"]
|
||||
path = sub/dragonfail
|
||||
url = https://git.nullgemm.fr/nullgemm/dragonfail.git
|
||||
[submodule "sub/termbox_next"]
|
||||
path = sub/termbox_next
|
||||
url = https://git.nullgemm.fr/nullgemm/termbox_next.git
|
||||
15
.github
Normal file
15
.github
Normal file
@@ -0,0 +1,15 @@
|
||||
[submodule "sub/argoat"]
|
||||
path = sub/argoat
|
||||
url = https://github.com/nullgemm/argoat.git
|
||||
[submodule "sub/configator"]
|
||||
path = sub/configator
|
||||
url = https://github.com/nullgemm/configator.git
|
||||
[submodule "sub/ctypes"]
|
||||
path = sub/ctypes
|
||||
url = https://github.com/nullgemm/ctypes.git
|
||||
[submodule "sub/dragonfail"]
|
||||
path = sub/dragonfail
|
||||
url = https://github.com/nullgemm/dragonfail.git
|
||||
[submodule "sub/termbox_next"]
|
||||
path = sub/termbox_next
|
||||
url = https://github.com/nullgemm/termbox_next.git
|
||||
68
.github/ISSUE_TEMPLATE/bug.yml
vendored
68
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -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.
|
||||
22
.github/ISSUE_TEMPLATE/feature.yml
vendored
22
.github/ISSUE_TEMPLATE/feature.yml
vendored
@@ -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
BIN
.github/screenshot.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,5 +1,4 @@
|
||||
.idea/
|
||||
zig-cache/
|
||||
zig-out/
|
||||
bin
|
||||
obj
|
||||
.gitmodules
|
||||
valgrind.log
|
||||
.zig-cache
|
||||
|
||||
450
build.zig
450
build.zig
@@ -1,450 +0,0 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const PatchMap = std.StringHashMap([]const u8);
|
||||
const InitSystem = enum {
|
||||
systemd,
|
||||
openrc,
|
||||
runit,
|
||||
s6,
|
||||
dinit,
|
||||
};
|
||||
|
||||
const min_zig_string = "0.14.0";
|
||||
const current_zig = builtin.zig_version;
|
||||
|
||||
// Implementing zig version detection through compile time
|
||||
comptime {
|
||||
const min_zig = std.SemanticVersion.parse(min_zig_string) catch unreachable;
|
||||
if (current_zig.order(min_zig) == .lt) {
|
||||
@compileError(std.fmt.comptimePrint("Your Zig version v{} does not meet the minimum build requirement of v{}", .{ current_zig, min_zig }));
|
||||
}
|
||||
}
|
||||
|
||||
const ly_version = std.SemanticVersion{ .major = 1, .minor = 1, .patch = 2 };
|
||||
|
||||
var dest_directory: []const u8 = undefined;
|
||||
var config_directory: []const u8 = undefined;
|
||||
var prefix_directory: []const u8 = undefined;
|
||||
var executable_name: []const u8 = undefined;
|
||||
var init_system: InitSystem = undefined;
|
||||
var default_tty_str: []const u8 = undefined;
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
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";
|
||||
prefix_directory = b.option([]const u8, "prefix_directory", "Specify a default prefix directory (default is /usr)") orelse "/usr";
|
||||
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 build_options = b.addOptions();
|
||||
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(u8, "tty", default_tty);
|
||||
build_options.addOption(bool, "enable_x11_support", enable_x11_support);
|
||||
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "ly",
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const zigini = b.dependency("zigini", .{ .target = target, .optimize = optimize });
|
||||
exe.root_module.addImport("zigini", zigini.module("zigini"));
|
||||
|
||||
exe.root_module.addOptions("build_options", build_options);
|
||||
|
||||
const clap = b.dependency("clap", .{ .target = target, .optimize = optimize });
|
||||
exe.root_module.addImport("clap", clap.module("clap"));
|
||||
|
||||
exe.addIncludePath(b.path("include"));
|
||||
exe.linkSystemLibrary("pam");
|
||||
if (enable_x11_support) exe.linkSystemLibrary("xcb");
|
||||
exe.linkLibC();
|
||||
|
||||
const translate_c = b.addTranslateC(.{
|
||||
.root_source_file = b.path("include/termbox2.h"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
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");
|
||||
exe.root_module.addImport("termbox2", termbox2);
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
if (b.args) |args| run_cmd.addArgs(args);
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
const installexe_step = b.step("installexe", "Install Ly and the selected init system service");
|
||||
installexe_step.makeFn = Installer(true).make;
|
||||
installexe_step.dependOn(b.getInstallStep());
|
||||
|
||||
const installnoconf_step = b.step("installnoconf", "Install Ly and the selected init system service, but not the configuration file");
|
||||
installnoconf_step.makeFn = Installer(false).make;
|
||||
installnoconf_step.dependOn(b.getInstallStep());
|
||||
|
||||
const uninstallexe_step = b.step("uninstallexe", "Uninstall Ly and remove the selected init system service");
|
||||
uninstallexe_step.makeFn = Uninstaller(true).make;
|
||||
|
||||
const uninstallnoconf_step = b.step("uninstallnoconf", "Uninstall Ly and remove the selected init system service, but keep the configuration directory");
|
||||
uninstallnoconf_step.makeFn = Uninstaller(false).make;
|
||||
}
|
||||
|
||||
pub fn Installer(install_config: bool) type {
|
||||
return struct {
|
||||
pub fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
|
||||
const allocator = step.owner.allocator;
|
||||
|
||||
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 ly_config_directory = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly" });
|
||||
|
||||
std.fs.cwd().makePath(ly_config_directory) catch {
|
||||
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_config_directory});
|
||||
};
|
||||
|
||||
const ly_lang_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/lang" });
|
||||
std.fs.cwd().makePath(ly_lang_path) catch {
|
||||
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_lang_path});
|
||||
};
|
||||
|
||||
{
|
||||
const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin" });
|
||||
std.fs.cwd().makePath(exe_path) catch {
|
||||
if (!std.mem.eql(u8, dest_directory, "")) {
|
||||
std.debug.print("warn: {s} already exists as a directory.\n", .{exe_path});
|
||||
}
|
||||
};
|
||||
|
||||
var executable_dir = std.fs.cwd().openDir(exe_path, .{}) catch unreachable;
|
||||
defer executable_dir.close();
|
||||
|
||||
try installFile("zig-out/bin/ly", executable_dir, exe_path, executable_name, .{});
|
||||
}
|
||||
|
||||
{
|
||||
var config_dir = std.fs.cwd().openDir(ly_config_directory, .{}) catch unreachable;
|
||||
defer config_dir.close();
|
||||
|
||||
if (install_config) {
|
||||
const patched_config = try patchFile(allocator, "res/config.ini", patch_map);
|
||||
try installText(patched_config, config_dir, ly_config_directory, "config.ini", .{});
|
||||
}
|
||||
|
||||
const patched_setup = try patchFile(allocator, "res/setup.sh", patch_map);
|
||||
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;
|
||||
defer lang_dir.close();
|
||||
|
||||
try installFile("res/lang/cat.ini", lang_dir, ly_lang_path, "cat.ini", .{});
|
||||
try installFile("res/lang/cs.ini", lang_dir, ly_lang_path, "cs.ini", .{});
|
||||
try installFile("res/lang/de.ini", lang_dir, ly_lang_path, "de.ini", .{});
|
||||
try installFile("res/lang/en.ini", lang_dir, ly_lang_path, "en.ini", .{});
|
||||
try installFile("res/lang/es.ini", lang_dir, ly_lang_path, "es.ini", .{});
|
||||
try installFile("res/lang/fr.ini", lang_dir, ly_lang_path, "fr.ini", .{});
|
||||
try installFile("res/lang/it.ini", lang_dir, ly_lang_path, "it.ini", .{});
|
||||
try installFile("res/lang/pl.ini", lang_dir, ly_lang_path, "pl.ini", .{});
|
||||
try installFile("res/lang/pt.ini", lang_dir, ly_lang_path, "pt.ini", .{});
|
||||
try installFile("res/lang/pt_BR.ini", lang_dir, ly_lang_path, "pt_BR.ini", .{});
|
||||
try installFile("res/lang/ro.ini", lang_dir, ly_lang_path, "ro.ini", .{});
|
||||
try installFile("res/lang/ru.ini", lang_dir, ly_lang_path, "ru.ini", .{});
|
||||
try installFile("res/lang/sr.ini", lang_dir, ly_lang_path, "sr.ini", .{});
|
||||
try installFile("res/lang/sv.ini", lang_dir, ly_lang_path, "sv.ini", .{});
|
||||
try installFile("res/lang/tr.ini", lang_dir, ly_lang_path, "tr.ini", .{});
|
||||
try installFile("res/lang/uk.ini", lang_dir, ly_lang_path, "uk.ini", .{});
|
||||
}
|
||||
|
||||
{
|
||||
const pam_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/pam.d" });
|
||||
std.fs.cwd().makePath(pam_path) catch {
|
||||
if (!std.mem.eql(u8, dest_directory, "")) {
|
||||
std.debug.print("warn: {s} already exists as a directory.\n", .{pam_path});
|
||||
}
|
||||
};
|
||||
|
||||
var pam_dir = std.fs.cwd().openDir(pam_path, .{}) catch unreachable;
|
||||
defer pam_dir.close();
|
||||
|
||||
try installFile("res/pam.d/ly", pam_dir, pam_path, "ly", .{ .override_mode = 0o644 });
|
||||
}
|
||||
}
|
||||
|
||||
fn install_service(allocator: std.mem.Allocator, patch_map: PatchMap) !void {
|
||||
switch (init_system) {
|
||||
.systemd => {
|
||||
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/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();
|
||||
|
||||
const patched_service = try patchFile(allocator, "res/ly.service", patch_map);
|
||||
try installText(patched_service, service_dir, service_path, "ly.service", .{ .mode = 0o644 });
|
||||
},
|
||||
.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);
|
||||
try installText(patched_service, service_dir, service_path, executable_name, .{ .mode = 0o755 });
|
||||
},
|
||||
.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 patched_conf = try patchFile(allocator, "res/ly-runit-service/conf", patch_map);
|
||||
try installText(patched_conf, service_dir, service_path, "conf", .{});
|
||||
|
||||
try installFile("res/ly-runit-service/finish", service_dir, service_path, "finish", .{ .override_mode = 0o755 });
|
||||
|
||||
const patched_run = try patchFile(allocator, "res/ly-runit-service/run", patch_map);
|
||||
try installText(patched_run, service_dir, service_path, "run", .{ .mode = 0o755 });
|
||||
|
||||
try 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"),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn getVersionStr(b: *std.Build, name: []const u8, version: std.SemanticVersion) ![]const u8 {
|
||||
const version_str = b.fmt("{d}.{d}.{d}", .{ version.major, version.minor, version.patch });
|
||||
|
||||
var status: u8 = undefined;
|
||||
const git_describe_raw = b.runAllowFail(&[_][]const u8{
|
||||
"git",
|
||||
"-C",
|
||||
b.build_root.path orelse ".",
|
||||
"describe",
|
||||
"--match",
|
||||
"*.*.*",
|
||||
"--tags",
|
||||
}, &status, .Ignore) catch {
|
||||
return version_str;
|
||||
};
|
||||
var git_describe = std.mem.trim(u8, git_describe_raw, " \n\r");
|
||||
git_describe = std.mem.trimLeft(u8, git_describe, "v");
|
||||
|
||||
switch (std.mem.count(u8, git_describe, "-")) {
|
||||
0 => {
|
||||
if (!std.mem.eql(u8, version_str, git_describe)) {
|
||||
std.debug.print("{s} version '{s}' does not match git tag: '{s}'\n", .{ name, version_str, git_describe });
|
||||
std.process.exit(1);
|
||||
}
|
||||
return version_str;
|
||||
},
|
||||
2 => {
|
||||
// Untagged development build (e.g. 0.10.0-dev.2025+ecf0050a9).
|
||||
var it = std.mem.splitScalar(u8, git_describe, '-');
|
||||
const tagged_ancestor = std.mem.trimLeft(u8, it.first(), "v");
|
||||
const commit_height = it.next().?;
|
||||
const commit_id = it.next().?;
|
||||
|
||||
const ancestor_ver = try std.SemanticVersion.parse(tagged_ancestor);
|
||||
if (version.order(ancestor_ver) != .gt) {
|
||||
std.debug.print("{s} version '{}' must be greater than tagged ancestor '{}'\n", .{ name, version, ancestor_ver });
|
||||
std.process.exit(1);
|
||||
}
|
||||
|
||||
// Check that the commit hash is prefixed with a 'g' (a Git convention).
|
||||
if (commit_id.len < 1 or commit_id[0] != 'g') {
|
||||
std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
|
||||
return version_str;
|
||||
}
|
||||
|
||||
// The version is reformatted in accordance with the https://semver.org specification.
|
||||
return b.fmt("{s}-dev.{s}+{s}", .{ version_str, commit_height, commit_id[1..] });
|
||||
},
|
||||
else => {
|
||||
std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
|
||||
return version_str;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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});
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
.{
|
||||
.name = .ly,
|
||||
.version = "1.1.2",
|
||||
.fingerprint = 0xa148ffcc5dc2cb59,
|
||||
.minimum_zig_version = "0.14.0",
|
||||
.dependencies = .{
|
||||
.clap = .{
|
||||
.url = "https://github.com/Hejsil/zig-clap/archive/refs/tags/0.10.0.tar.gz",
|
||||
.hash = "clap-0.10.0-oBajB434AQBDh-Ei3YtoKIRxZacVPF1iSwp3IX_ZB8f0",
|
||||
},
|
||||
.zigini = .{
|
||||
.url = "https://github.com/Kawaii-Ash/zigini/archive/2ed3d417f17fab5b0ee8cad8a63c6d62d7ac1042.tar.gz",
|
||||
.hash = "zigini-0.3.1-BSkB7XJGAAB2E-sKyzhTaQCBlYBL8yqzE4E_jmSY99sC",
|
||||
},
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
4321
include/termbox2.h
4321
include/termbox2.h
File diff suppressed because it is too large
Load Diff
78
ly.spec.rpkg
Normal file
78
ly.spec.rpkg
Normal file
@@ -0,0 +1,78 @@
|
||||
%define relabel_files() \
|
||||
restorecon -R /usr/bin/ly; \
|
||||
|
||||
%define selinux_policyver 3.14.6-34
|
||||
|
||||
Name: {{{ git_dir_name }}}
|
||||
Version: {{{ git_dir_version }}}
|
||||
Release: 1%{?dist}
|
||||
Summary: A TUI display manager
|
||||
License: WTFPL
|
||||
URL: https://github.com/nullgemm/ly
|
||||
VCS: {{{ git_dir_vcs }}}
|
||||
Source: {{{ git_dir_pack }}}
|
||||
BuildRequires: libxcb-devel
|
||||
BuildRequires: pam-devel
|
||||
BuildRequires: make
|
||||
BuildRequires: git
|
||||
BuildRequires: gcc
|
||||
BuildRequires: selinux-policy-devel
|
||||
Requires: libxcb
|
||||
Requires: pam
|
||||
|
||||
%description
|
||||
Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD.
|
||||
|
||||
%prep
|
||||
git clone https://github.com/dhalucario/ly.git ly
|
||||
cd ly
|
||||
# git checkout v0.5.2
|
||||
make github
|
||||
|
||||
%build
|
||||
cd ly
|
||||
make
|
||||
|
||||
%install
|
||||
cd ly
|
||||
mkdir -p %{buildroot}/etc/
|
||||
mkdir -p %{buildroot}/usr/bin/
|
||||
mkdir -p %{buildroot}/usr/lib/systemd/system/
|
||||
mkdir -p %{buildroot}/etc/pam.d/
|
||||
DESTDIR="%{buildroot}" make install
|
||||
DESTDIR="%{buildroot}" make installselinux
|
||||
chmod -x %{buildroot}/etc/ly/config.ini
|
||||
chmod -x %{buildroot}/etc/ly/lang/*
|
||||
|
||||
%post
|
||||
semodule -n -i /usr/share/selinux/packages/ly.pp
|
||||
if /usr/sbin/selinuxenabled ; then
|
||||
/usr/sbin/load_policy
|
||||
%relabel_files
|
||||
|
||||
fi;
|
||||
exit 0
|
||||
|
||||
%postun
|
||||
if [ $1 -eq 0 ]; then
|
||||
semodule -n -r ly
|
||||
fi;
|
||||
exit 0
|
||||
|
||||
%files
|
||||
/usr/bin/ly
|
||||
/usr/lib/systemd/system/ly.service
|
||||
/etc/ly/lang/es.ini
|
||||
/etc/ly/lang/pt.ini
|
||||
/etc/ly/lang/ru.ini
|
||||
/etc/ly/lang/en.ini
|
||||
/etc/ly/lang/fr.ini
|
||||
/etc/ly/lang/ro.ini
|
||||
/etc/ly/xsetup.sh
|
||||
/etc/ly/wsetup.sh
|
||||
/etc/ly/config.ini
|
||||
/etc/pam.d/ly
|
||||
/usr/share/selinux/packages/ly.pp
|
||||
|
||||
%changelog
|
||||
{{{ git_dir_changelog }}}
|
||||
132
makefile
Normal file
132
makefile
Normal file
@@ -0,0 +1,132 @@
|
||||
NAME = ly
|
||||
CC = gcc
|
||||
FLAGS = -std=c99 -pedantic -g
|
||||
FLAGS+= -Wall -Wextra -Werror=vla -Wno-unused-parameter
|
||||
#FLAGS+= -DDEBUG
|
||||
FLAGS+= -DGIT_VERSION_STRING=\"$(shell git describe --long --tags | sed 's/\([^-]*-g\)/r\1/;s/-/./g')\"
|
||||
LINK = -lpam -lxcb
|
||||
VALGRIND = --show-leak-kinds=all --track-origins=yes --leak-check=full --suppressions=../res/valgrind.supp
|
||||
CMD = ./$(NAME)
|
||||
|
||||
OS:= $(shell uname -s)
|
||||
ifeq ($(OS), Linux)
|
||||
FLAGS+= -D_DEFAULT_SOURCE
|
||||
endif
|
||||
|
||||
BIND = bin
|
||||
OBJD = obj
|
||||
SRCD = src
|
||||
SUBD = sub
|
||||
RESD = res
|
||||
TESTD = tests
|
||||
|
||||
DATADIR ?= ${DESTDIR}/etc/ly
|
||||
FLAGS+= -DDATADIR=\"$(DATADIR)\"
|
||||
|
||||
INCL = -I$(SRCD)
|
||||
INCL+= -I$(SUBD)/ctypes
|
||||
INCL+= -I$(SUBD)/argoat/src
|
||||
INCL+= -I$(SUBD)/configator/src
|
||||
INCL+= -I$(SUBD)/dragonfail/src
|
||||
INCL+= -I$(SUBD)/termbox_next/src
|
||||
|
||||
SRCS = $(SRCD)/main.c
|
||||
SRCS += $(SRCD)/config.c
|
||||
SRCS += $(SRCD)/draw.c
|
||||
SRCS += $(SRCD)/inputs.c
|
||||
SRCS += $(SRCD)/login.c
|
||||
SRCS += $(SRCD)/utils.c
|
||||
SRCS += $(SUBD)/argoat/src/argoat.c
|
||||
SRCS += $(SUBD)/configator/src/configator.c
|
||||
SRCS += $(SUBD)/dragonfail/src/dragonfail.c
|
||||
|
||||
SRCS_OBJS:= $(patsubst %.c,$(OBJD)/%.o,$(SRCS))
|
||||
SRCS_OBJS+= $(SUBD)/termbox_next/bin/termbox.a
|
||||
|
||||
.PHONY: final
|
||||
final: $(BIND)/$(NAME)
|
||||
|
||||
$(OBJD)/%.o: %.c
|
||||
@echo "building object $@"
|
||||
@mkdir -p $(@D)
|
||||
@$(CC) $(INCL) $(FLAGS) -c -o $@ $<
|
||||
|
||||
$(SUBD)/termbox_next/bin/termbox.a:
|
||||
@echo "building static object $@"
|
||||
@(cd $(SUBD)/termbox_next && $(MAKE))
|
||||
|
||||
$(BIND)/$(NAME): $(SRCS_OBJS)
|
||||
@echo "compiling executable $@"
|
||||
@mkdir -p $(@D)
|
||||
@$(CC) -o $@ $^ $(LINK)
|
||||
|
||||
run:
|
||||
@cd $(BIND) && $(CMD)
|
||||
|
||||
leak: leakgrind
|
||||
leakgrind: $(BIND)/$(NAME)
|
||||
@rm -f valgrind.log
|
||||
@cd $(BIND) && valgrind $(VALGRIND) 2> ../valgrind.log $(CMD)
|
||||
@less valgrind.log
|
||||
|
||||
install: $(BIND)/$(NAME)
|
||||
@echo "installing"
|
||||
@install -dZ ${DESTDIR}/etc/ly
|
||||
@install -DZ $(BIND)/$(NAME) -t ${DESTDIR}/usr/bin
|
||||
@install -DZ $(RESD)/config.ini -t ${DESTDIR}/etc/ly
|
||||
@install -DZ $(RESD)/xsetup.sh -t $(DATADIR)
|
||||
@install -DZ $(RESD)/wsetup.sh -t $(DATADIR)
|
||||
@install -dZ $(DATADIR)/lang
|
||||
@install -DZ $(RESD)/lang/* -t $(DATADIR)/lang
|
||||
@install -DZ $(RESD)/ly.service -m 644 -t ${DESTDIR}/usr/lib/systemd/system
|
||||
@install -DZ $(RESD)/pam.d/ly -m 644 -t ${DESTDIR}/etc/pam.d
|
||||
|
||||
installnoconf: $(BIND)/$(NAME)
|
||||
@echo "installing without the configuration file"
|
||||
@install -dZ ${DESTDIR}/etc/ly
|
||||
@install -DZ $(BIND)/$(NAME) -t ${DESTDIR}/usr/bin
|
||||
@install -DZ $(RESD)/xsetup.sh -t $(DATADIR)
|
||||
@install -DZ $(RESD)/wsetup.sh -t $(DATADIR)
|
||||
@install -dZ $(DATADIR)/lang
|
||||
@install -DZ $(RESD)/lang/* -t $(DATADIR)/lang
|
||||
@install -DZ $(RESD)/ly.service -m 644 -t ${DESTDIR}/usr/lib/systemd/system
|
||||
@install -DZ $(RESD)/pam.d/ly -m 644 -t ${DESTDIR}/etc/pam.d
|
||||
|
||||
installselinux:
|
||||
@echo "installing selinux modules"
|
||||
@make -f /usr/share/selinux/devel/Makefile ly.pp
|
||||
@install -DZ ly.pp ${DESTDIR}/usr/share/selinux/packages/ly.pp
|
||||
|
||||
uninstall:
|
||||
@echo "uninstalling"
|
||||
@rm -rf ${DESTDIR}/etc/ly
|
||||
@rm -rf $(DATADIR)
|
||||
@rm -f ${DESTDIR}/usr/bin/ly
|
||||
@rm -f ${DESTDIR}/usr/lib/systemd/system/ly.service
|
||||
@rm -f ${DESTDIR}/etc/pam.d/ly
|
||||
|
||||
clean:
|
||||
@echo "cleaning"
|
||||
@rm -rf $(BIND) $(OBJD) valgrind.log
|
||||
@(cd $(SUBD)/termbox_next && $(MAKE) clean)
|
||||
|
||||
remotes:
|
||||
@echo "registering remotes"
|
||||
@git remote add github git@github.com:nullgemm/$(NAME).git
|
||||
@git remote add gitea ssh://git@git.nullgem.fr:2999/nullgemm/$(NAME).git
|
||||
|
||||
github:
|
||||
@echo "sourcing submodules from https://github.com"
|
||||
@cp .github .gitmodules
|
||||
@git submodule sync
|
||||
@git submodule update --init --remote
|
||||
@cd $(SUBD)/argoat && make github
|
||||
@git submodule update --init --recursive --remote
|
||||
|
||||
gitea:
|
||||
@echo "sourcing submodules from personal server"
|
||||
@cp .gitea .gitmodules
|
||||
@git submodule sync
|
||||
@git submodule update --init --remote
|
||||
@cd $(SUBD)/argoat && make gitea
|
||||
@git submodule update --init --recursive --remote
|
||||
258
readme.md
258
readme.md
@@ -1,42 +1,39 @@
|
||||
# Ly - a TUI display manager
|
||||
|
||||
## 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 is a lightweight TUI (ncurses-like) display manager for Linux and BSD.
|
||||
|
||||
## Dependencies
|
||||
- Compile-time:
|
||||
- zig 0.14.0
|
||||
- libc
|
||||
- pam
|
||||
- xcb (optional, required by default; needed for X11 support)
|
||||
- Runtime (with default config):
|
||||
- xorg
|
||||
- xorg-xauth
|
||||
- shutdown
|
||||
- brightnessctl
|
||||
- a C99 compiler (tested with tcc and gcc)
|
||||
- a C standard library
|
||||
- GNU make
|
||||
- pam
|
||||
- xcb
|
||||
- xorg
|
||||
- xorg-xauth
|
||||
- mcookie
|
||||
- tput
|
||||
- shutdown
|
||||
|
||||
### Debian
|
||||
```
|
||||
# apt install build-essential libpam0g-dev libxcb-xkb-dev
|
||||
```
|
||||
|
||||
### Fedora
|
||||
**Warning**: You may encounter issues with SELinux on Fedora.
|
||||
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
|
||||
```
|
||||
On Debian-based distros running `apt install build-essential libpam0g-dev libxcb-xkb-dev` as root should install all the dependencies for you.
|
||||
|
||||
## Support
|
||||
The following desktop environments were tested with success:
|
||||
|
||||
[Wayland Environments](#supported-wayland-environments)
|
||||
|
||||
[X11 Environments](#supported-x11-environments)
|
||||
The following desktop environments were tested with success
|
||||
- budgie
|
||||
- cinnamon
|
||||
- deepin
|
||||
- enlightenment
|
||||
- gnome
|
||||
- i3
|
||||
- kde
|
||||
- lxde
|
||||
- lxqt
|
||||
- mate
|
||||
- sway
|
||||
- xfce
|
||||
- pantheon
|
||||
- maxx
|
||||
- windowmaker
|
||||
|
||||
Ly should work with any X desktop environment, and provides
|
||||
basic wayland support (sway works very well, for example).
|
||||
@@ -50,162 +47,39 @@ changing the source code won't be necessary :)
|
||||
## Cloning and Compiling
|
||||
Clone the repository
|
||||
```
|
||||
$ git clone https://codeberg.org/AnErrupTion/ly
|
||||
git clone https://github.com/nullgemm/ly.git
|
||||
```
|
||||
|
||||
Change the directory to ly
|
||||
Fetch submodules
|
||||
```
|
||||
$ cd ly
|
||||
make github
|
||||
```
|
||||
|
||||
Compile
|
||||
```
|
||||
$ zig build
|
||||
make
|
||||
```
|
||||
|
||||
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
|
||||
sudo make run
|
||||
```
|
||||
|
||||
**Important**: Running Ly in a terminal emulator as root is *not* recommended. If you
|
||||
want to properly test Ly, please enable its service (as described below) and reboot
|
||||
your machine.
|
||||
|
||||
Install Ly for systemd-based systems (the default)
|
||||
Install Ly and the provided systemd service file
|
||||
```
|
||||
# zig build installexe
|
||||
```
|
||||
|
||||
Instead of DISPLAY_MANAGER you need to add your DM:
|
||||
- gdm.service
|
||||
- sddm.service
|
||||
- lightdm.service
|
||||
```
|
||||
# systemctl disable DISPLAY_MANAGER
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Enable the service
|
||||
```
|
||||
# systemctl enable ly.service
|
||||
sudo systemctl enable ly.service
|
||||
```
|
||||
|
||||
If you need to switch between ttys after Ly's start you also have to
|
||||
disable getty on Ly's tty to prevent "login" from spawning on top of it
|
||||
```
|
||||
# systemctl disable getty@tty2.service
|
||||
```
|
||||
|
||||
### OpenRC
|
||||
**NOTE 1**: On Gentoo, Ly will disable the `display-manager-init` service in order to run.
|
||||
|
||||
Clone, compile and test.
|
||||
|
||||
Install Ly and the provided OpenRC service
|
||||
```
|
||||
# zig build installexe -Dinit_system=openrc
|
||||
```
|
||||
|
||||
Enable the service
|
||||
```
|
||||
# rc-update add ly
|
||||
```
|
||||
|
||||
You can edit which tty Ly will start on by editing the `tty` option in the configuration file.
|
||||
|
||||
If you choose a tty that already has a login/getty running (has a basic login prompt),
|
||||
then you have to disable getty, so it doesn't respawn on top of ly
|
||||
```
|
||||
# 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
|
||||
```
|
||||
# zig build installexe -Dinit_system=runit
|
||||
# ln -s /etc/sv/ly /var/service/
|
||||
```
|
||||
|
||||
By default, ly will run on tty2. To change the tty it must be set in `/etc/ly/config.ini`
|
||||
|
||||
You should as well disable your existing display manager service if needed, e.g.:
|
||||
|
||||
```
|
||||
# rm /var/service/lxdm
|
||||
```
|
||||
|
||||
The agetty service for the tty console where you are running ly should be disabled.
|
||||
For instance, if you are running ly on tty2 (that's the default, check your `/etc/ly/config.ini`)
|
||||
you should disable the agetty-tty2 service like this:
|
||||
|
||||
```
|
||||
# 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
|
||||
You can also install Ly without overrding the current configuration file. That's called
|
||||
*updating*. To update, simply run:
|
||||
|
||||
```
|
||||
# zig build installnoconf
|
||||
```
|
||||
|
||||
You can, of course, still select the init system of your choice when using this command.
|
||||
|
||||
## Arch Linux Installation
|
||||
You can install ly from the [`[extra]` repos](https://archlinux.org/packages/extra/x86_64/ly/):
|
||||
```
|
||||
# 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
|
||||
sudo systemctl disable getty@tty2.service
|
||||
```
|
||||
|
||||
## Configuration
|
||||
@@ -220,54 +94,26 @@ while on the desktop field (above the login field).
|
||||
## .xinitrc
|
||||
If your .xinitrc doesn't work make sure it is executable and includes a shebang.
|
||||
This file is supposed to be a shell script! Quoting from xinit's man page:
|
||||
|
||||
> If no specific client program is given on the command line, xinit will look for a file in the user's home directory called .xinitrc to run as a shell script to start up client programs.
|
||||
|
||||
On Arch Linux, the example .xinitrc (/etc/X11/xinit/xinitrc) starts like this:
|
||||
```
|
||||
If no specific client program is given on the command line, xinit will look for
|
||||
a file in the user's home directory called .xinitrc to run as a shell script to
|
||||
start up client programs.
|
||||
```
|
||||
On ArchLinux, the example .xinitrc (/etc/X11/xinit/xinitrc) starts like this:
|
||||
```
|
||||
#!/bin/sh
|
||||
```
|
||||
|
||||
## Tips
|
||||
- The numlock and capslock state is printed in the top-right corner.
|
||||
- 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
|
||||
(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
|
||||
The numlock and capslock state is printed in the top-right corner.
|
||||
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
|
||||
(this file is launched with X to configure the display properly).
|
||||
|
||||
## PSX DOOM fire animation
|
||||
To enable the famous PSX DOOM fire described by [Fabien Sanglard](http://fabiensanglard.net/doom_fire_psx/index.html),
|
||||
just uncomment `animate = true` in `/etc/ly/config.ini`. You may also
|
||||
disable the main box borders with `hide_borders = true`.
|
||||
|
||||
## Additional Information
|
||||
The name "Ly" is a tribute to the fairy from the game Rayman.
|
||||
|
||||
284
res/config.ini
284
res/config.ini
@@ -1,242 +1,104 @@
|
||||
# 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).
|
||||
# animation enabled
|
||||
#animate = false
|
||||
#animate = true
|
||||
|
||||
# Allow empty password or not when authenticating
|
||||
allow_empty_password = true
|
||||
# the active animation (only animation '0' available for now)
|
||||
#animation = 0
|
||||
|
||||
# The active animation
|
||||
# none -> Nothing
|
||||
# doom -> PSX DOOM fire
|
||||
# matrix -> CMatrix
|
||||
# colormix -> Color mixing shader
|
||||
animation = none
|
||||
# the char used to mask the password
|
||||
#asterisk = *
|
||||
#asterisk = o
|
||||
|
||||
# Stop the animation after some time
|
||||
# 0 -> Run forever
|
||||
# 1..2e12 -> Stop the animation after this many seconds
|
||||
animation_timeout_sec = 0
|
||||
# background color id
|
||||
#bg = 0
|
||||
|
||||
# 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 = *
|
||||
# blank main box
|
||||
#blank_box = true
|
||||
|
||||
# The number of failed authentications before a special animation is played... ;)
|
||||
auth_fails = 10
|
||||
# erase password input on failure
|
||||
#blank_password = false
|
||||
#blank_password = true
|
||||
|
||||
# Background color id
|
||||
bg = 0x00000000
|
||||
# console path
|
||||
#console_dev = /dev/console
|
||||
|
||||
# Change the state and language of the big clock
|
||||
# none -> Disabled (default)
|
||||
# en -> English
|
||||
# fa -> Farsi
|
||||
bigclock = none
|
||||
# input active by default on startup
|
||||
#default_input = 2
|
||||
|
||||
# Blank main box background
|
||||
# Setting to false will make it transparent
|
||||
blank_box = true
|
||||
# foreground color id
|
||||
#fg = 9
|
||||
|
||||
# Border foreground color id
|
||||
border_fg = 0x00FFFFFF
|
||||
# remove main box borders
|
||||
#hide_borders = false
|
||||
#hide_borders = true
|
||||
|
||||
# Title to show at the top of the main box
|
||||
# If set to null, none will be shown
|
||||
box_title = null
|
||||
# number of visible chars on an input
|
||||
#input_len = 34
|
||||
|
||||
# Brightness increase command
|
||||
brightness_down_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q s 10%-
|
||||
# active language
|
||||
#lang = en
|
||||
#lang = fr
|
||||
|
||||
# Brightness decrease key, or null to disable
|
||||
brightness_down_key = F5
|
||||
# load the saved desktop and login
|
||||
#load = true
|
||||
|
||||
# Brightness increase command
|
||||
brightness_up_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q s +10%
|
||||
# main box margins
|
||||
#margin_box_h = 2
|
||||
#margin_box_v = 1
|
||||
|
||||
# Brightness increase key, or null to disable
|
||||
brightness_up_key = F6
|
||||
# total input sizes
|
||||
#max_desktop_len = 100
|
||||
#max_login_len = 255
|
||||
#max_password_len = 255
|
||||
|
||||
# Erase password input on failure
|
||||
clear_password = false
|
||||
# cookie generator
|
||||
#mcookie_cmd = /usr/bin/mcookie
|
||||
|
||||
# Format string for clock in top right corner (see strftime specification). Example: %c
|
||||
# If null, the clock won't be shown
|
||||
clock = null
|
||||
# event timeout in milliseconds
|
||||
#min_refresh_delta = 5
|
||||
|
||||
# CMatrix animation foreground color id
|
||||
cmatrix_fg = 0x0000FF00
|
||||
# default path
|
||||
#path = /sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin
|
||||
|
||||
# CMatrix animation minimum codepoint. It uses a 16-bit integer
|
||||
# For Japanese characters for example, you can use 0x3000 here
|
||||
cmatrix_min_codepoint = 0x21
|
||||
# command executed when pressing F2
|
||||
#restart_cmd = /sbin/shutdown -r now
|
||||
|
||||
# CMatrix animation maximum codepoint. It uses a 16-bit integer
|
||||
# For Japanese characters for example, you can use 0x30FF here
|
||||
cmatrix_max_codepoint = 0x7B
|
||||
# save the current desktop and login as defaults
|
||||
#save = true
|
||||
|
||||
# Color mixing animation first color id
|
||||
colormix_col1 = 0x00FF0000
|
||||
# file in which to save and load the default desktop and login
|
||||
#save_file = /etc/ly/save
|
||||
|
||||
# Color mixing animation second color id
|
||||
colormix_col2 = 0x000000FF
|
||||
# service name (set to ly to use the provided pam config file)
|
||||
#service_name = ly
|
||||
|
||||
# Color mixing animation third color id
|
||||
colormix_col3 = 0x20000000
|
||||
# command executed when pressing F1
|
||||
#shutdown_cmd = /sbin/shutdown -a now
|
||||
|
||||
# Console path
|
||||
console_dev = /dev/console
|
||||
# terminal reset command (tput is faster)
|
||||
#term_reset_cmd = /usr/bin/tput reset
|
||||
|
||||
# Input box active by default on startup
|
||||
# Available inputs: info_line, session, login, password
|
||||
default_input = login
|
||||
# tty in use
|
||||
#tty = 2
|
||||
|
||||
# DOOM animation top color (low intensity flames)
|
||||
doom_top_color = 0x00FF0000
|
||||
# wayland setup command
|
||||
#wayland_cmd = /etc/ly/wsetup.sh
|
||||
|
||||
# DOOM animation middle color (medium intensity flames)
|
||||
doom_middle_color = 0x00FFFF00
|
||||
# add wayland specifier to session names
|
||||
#wayland_specifier = false
|
||||
#wayland_specifier = true
|
||||
|
||||
# DOOM animation bottom color (high intensity flames)
|
||||
doom_bottom_color = 0x00FFFFFF
|
||||
# wayland desktop environments
|
||||
#waylandsessions = /usr/share/wayland-sessions
|
||||
|
||||
# Error background color id
|
||||
error_bg = 0x00000000
|
||||
# xorg server command
|
||||
#x_cmd = /usr/bin/X
|
||||
|
||||
# Error foreground color id
|
||||
# Default is red and bold
|
||||
error_fg = 0x01FF0000
|
||||
# xorg setup command
|
||||
#x_cmd_setup = /etc/ly/xsetup.sh
|
||||
|
||||
# Foreground color id
|
||||
fg = 0x00FFFFFF
|
||||
# xorg xauthority edition tool
|
||||
#xauth_cmd = /usr/bin/xauth
|
||||
|
||||
# Remove main box borders
|
||||
hide_borders = false
|
||||
|
||||
# Remove power management command hints
|
||||
hide_key_hints = false
|
||||
|
||||
# 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_len = 34
|
||||
|
||||
# Active language
|
||||
# Available languages are found in $CONFIG_DIRECTORY/ly/lang/
|
||||
lang = en
|
||||
|
||||
# Load the saved desktop and username
|
||||
load = true
|
||||
|
||||
# Command executed when logging in
|
||||
# If null, no command will be executed
|
||||
# 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
|
||||
# If null, no command will be executed
|
||||
# Important: the session will already be terminated when this command is executed, so
|
||||
# no need to add `exec "$@"` at the end
|
||||
logout_cmd = null
|
||||
|
||||
# Main box horizontal margin
|
||||
margin_box_h = 2
|
||||
|
||||
# Main box vertical margin
|
||||
margin_box_v = 1
|
||||
|
||||
# Event timeout in milliseconds
|
||||
min_refresh_delta = 5
|
||||
|
||||
# Set numlock on/off at startup
|
||||
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 = ly
|
||||
|
||||
# Session log file path
|
||||
# This will contain stdout and stderr of Wayland sessions
|
||||
# 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
|
||||
setup_cmd = $CONFIG_DIRECTORY/ly/setup.sh
|
||||
|
||||
# Command executed when pressing shutdown_key
|
||||
shutdown_cmd = /sbin/shutdown -a now
|
||||
|
||||
# Specifies the key used for shutdown (F1-F12)
|
||||
shutdown_key = F1
|
||||
|
||||
# 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
|
||||
# You can specify multiple directories,
|
||||
# e.g. /usr/share/wayland-sessions:/usr/local/share/wayland-sessions
|
||||
waylandsessions = $PREFIX_DIRECTORY/share/wayland-sessions
|
||||
|
||||
# Xorg server command
|
||||
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
|
||||
|
||||
# Xorg desktop environments
|
||||
# You can specify multiple directories,
|
||||
# e.g. /usr/share/xsessions:/usr/local/share/xsessions
|
||||
xsessions = $PREFIX_DIRECTORY/share/xsessions
|
||||
# xorg desktop environments
|
||||
#xsessions = /usr/share/xsessions
|
||||
|
||||
@@ -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
|
||||
@@ -1,64 +0,0 @@
|
||||
authenticating = autenticant...
|
||||
brightness_down = abaixar brillantor
|
||||
brightness_up = apujar brillantor
|
||||
capslock = Bloq Majús
|
||||
err_alloc = assignació de memòria fallida
|
||||
err_bounds = índex fora de límits
|
||||
err_brightness_change = error en canviar la brillantor
|
||||
err_chdir = error en obrir la carpeta home
|
||||
|
||||
err_console_dev = error en accedir a la consola
|
||||
err_dgn_oob = missatge de registre
|
||||
err_domain = domini invàlid
|
||||
|
||||
err_envlist = error en obtenir l'envlist
|
||||
err_hostname = error en obtenir el nom de l'amfitrió
|
||||
err_mlock = error en bloquejar la memòria de clau
|
||||
err_null = punter nul
|
||||
err_numlock = error en establir el Bloq num
|
||||
err_pam = error en la transacció pam
|
||||
err_pam_abort = transacció pam avortada
|
||||
err_pam_acct_expired = compte expirat
|
||||
err_pam_auth = error d'autenticació
|
||||
err_pam_authinfo_unavail = error en obtenir la informació de l'usuari
|
||||
err_pam_authok_reqd = token expirat
|
||||
err_pam_buf = error en la memòria intermèdia
|
||||
err_pam_cred_err = error en establir les credencials
|
||||
err_pam_cred_expired = credencials expirades
|
||||
err_pam_cred_insufficient = credencials insuficients
|
||||
err_pam_cred_unavail = error en obtenir credencials
|
||||
err_pam_maxtries = s'ha assolit al nombre màxim d'intents
|
||||
err_pam_perm_denied = permís denegat
|
||||
err_pam_session = error de sessió
|
||||
err_pam_sys = error de sistema
|
||||
err_pam_user_unknown = usuari desconegut
|
||||
err_path = error en establir la ruta
|
||||
err_perm_dir = error en canviar el directori actual
|
||||
err_perm_group = error en degradar els permisos de grup
|
||||
err_perm_user = error en degradar els permisos de l'usuari
|
||||
err_pwnam = error en obtenir la informació de l'usuari
|
||||
|
||||
|
||||
err_unknown = ha ocorregut un error desconegut
|
||||
err_user_gid = error en establir el GID de l'usuari
|
||||
err_user_init = error en inicialitzar usuari
|
||||
err_user_uid = error en establir l'UID de l'usuari
|
||||
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ó
|
||||
logout = sessió tancada
|
||||
no_x11_support = el suport per x11 ha estat desactivat en la compilació
|
||||
normal = normal
|
||||
numlock = Bloq Num
|
||||
|
||||
password = Clau
|
||||
restart = reiniciar
|
||||
shell = shell
|
||||
shutdown = aturar
|
||||
sleep = suspendre
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
|
||||
|
||||
capslock = capslock
|
||||
err_alloc = alokace paměti selhala
|
||||
err_bounds = index je mimo hranice pole
|
||||
|
||||
err_chdir = nelze otevřít domovský adresář
|
||||
|
||||
err_console_dev = chyba při přístupu do konzole
|
||||
err_dgn_oob = zpráva protokolu
|
||||
err_domain = neplatná doména
|
||||
|
||||
|
||||
err_hostname = nelze získat název hostitele
|
||||
err_mlock = uzamčení paměti hesel selhalo
|
||||
err_null = nulový ukazatel
|
||||
|
||||
err_pam = pam transakce selhala
|
||||
err_pam_abort = pam transakce přerušena
|
||||
err_pam_acct_expired = platnost účtu vypršela
|
||||
err_pam_auth = chyba autentizace
|
||||
err_pam_authinfo_unavail = nelze získat informace o uživateli
|
||||
err_pam_authok_reqd = platnost tokenu vypršela
|
||||
err_pam_buf = chyba vyrovnávací paměti
|
||||
err_pam_cred_err = nelze nastavit pověření
|
||||
err_pam_cred_expired = platnost pověření vypršela
|
||||
err_pam_cred_insufficient = nedostatečné pověření
|
||||
err_pam_cred_unavail = nepodařilo se získat pověření
|
||||
err_pam_maxtries = byl dosažen maximální počet pokusů
|
||||
err_pam_perm_denied = přístup odepřen
|
||||
err_pam_session = chyba relace
|
||||
err_pam_sys = systemová chyba
|
||||
err_pam_user_unknown = neznámý uživatel
|
||||
err_path = nepodařilo se nastavit cestu
|
||||
err_perm_dir = nepodařilo se změnit adresář
|
||||
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_pwnam = nelze získat informace o uživateli
|
||||
|
||||
|
||||
|
||||
err_user_gid = nastavení GID uživatele selhalo
|
||||
err_user_init = inicializace uživatele selhala
|
||||
err_user_uid = nastavení UID uživateli selhalo
|
||||
|
||||
|
||||
err_xsessions_dir = nepodařilo se najít složku relací
|
||||
err_xsessions_open = nepodařilo se otevřít složku relací
|
||||
|
||||
login = uživatel
|
||||
logout = odhlášen
|
||||
|
||||
|
||||
numlock = numlock
|
||||
|
||||
password = heslo
|
||||
restart = restartovat
|
||||
shell = příkazový řádek
|
||||
shutdown = vypnout
|
||||
|
||||
wayland = wayland
|
||||
|
||||
xinitrc = xinitrc
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
|
||||
|
||||
capslock = Feststelltaste
|
||||
err_alloc = Speicherzuweisung fehlgeschlagen
|
||||
err_bounds = Listenindex ist außerhalb des Bereichs
|
||||
|
||||
err_chdir = Fehler beim oeffnen des home-ordners
|
||||
|
||||
err_console_dev = Zugriff auf die Konsole fehlgeschlagen
|
||||
err_dgn_oob = Protokoll Nachricht
|
||||
err_domain = Unzulaessige domain
|
||||
|
||||
|
||||
err_hostname = Holen des Hostnames fehlgeschlagen
|
||||
err_mlock = Abschließen des Passwortspeichers fehlgeschlagen
|
||||
err_null = Null Zeiger
|
||||
|
||||
err_pam = pam Transaktion fehlgeschlagen
|
||||
err_pam_abort = pam Transaktion abgebrochen
|
||||
err_pam_acct_expired = Benutzerkonto abgelaufen
|
||||
err_pam_auth = Authentifizierungs Fehler
|
||||
err_pam_authinfo_unavail = holen der Benutzerinformationen fehlgeschlagen
|
||||
err_pam_authok_reqd = Schluessel abgelaufen
|
||||
err_pam_buf = Speicherpufferfehler
|
||||
err_pam_cred_err = Fehler beim setzen der Anmeldedaten
|
||||
err_pam_cred_expired = Anmeldedaten abgelaufen
|
||||
err_pam_cred_insufficient = Anmeldedaten unzureichend
|
||||
err_pam_cred_unavail = Fehler beim holen der Anmeldedaten
|
||||
err_pam_maxtries = Maximale Versuche erreicht
|
||||
err_pam_perm_denied = Zugriff Verweigert
|
||||
err_pam_session = Sitzungsfehler
|
||||
err_pam_sys = Systemfehler
|
||||
err_pam_user_unknown = Unbekannter Nutzer
|
||||
err_path = Fehler beim setzen des Pfades
|
||||
err_perm_dir = Fehler beim wechseln des Ordners
|
||||
err_perm_group = Fehler beim heruntersetzen der Gruppen Berechtigungen
|
||||
err_perm_user = Fehler beim heruntersetzen der Nutzer Berechtigungen
|
||||
err_pwnam = Holen der Benutzerinformationen fehlgeschlagen
|
||||
|
||||
|
||||
|
||||
err_user_gid = Fehler beim setzen der Gruppen Id des Nutzers
|
||||
err_user_init = Initialisierung des Nutzers fehlgeschlagen
|
||||
err_user_uid = Setzen der Benutzer Id fehlgeschlagen
|
||||
|
||||
|
||||
err_xsessions_dir = Fehler beim finden des Sitzungsordners
|
||||
err_xsessions_open = Fehler beim öffnen des Sitzungsordners
|
||||
|
||||
login = Anmelden
|
||||
logout = Abgemeldet
|
||||
|
||||
|
||||
numlock = Numtaste
|
||||
|
||||
password = Passwort
|
||||
restart = Neustarten
|
||||
shell = shell
|
||||
shutdown = Herunterfahren
|
||||
|
||||
wayland = wayland
|
||||
|
||||
xinitrc = xinitrc
|
||||
@@ -1,21 +1,13 @@
|
||||
authenticating = authenticating...
|
||||
brightness_down = decrease brightness
|
||||
brightness_up = increase brightness
|
||||
capslock = capslock
|
||||
err_alloc = failed memory allocation
|
||||
err_bounds = out-of-bounds index
|
||||
err_brightness_change = failed to change brightness
|
||||
err_chdir = failed to open home folder
|
||||
err_config = unable to parse config file
|
||||
err_console_dev = failed to access console
|
||||
err_dgn_oob = log message
|
||||
err_domain = invalid domain
|
||||
err_empty_password = empty password not allowed
|
||||
err_envlist = failed to get envlist
|
||||
err_hostname = failed to get hostname
|
||||
err_mlock = failed to lock password memory
|
||||
err_null = null pointer
|
||||
err_numlock = failed to set numlock
|
||||
err_pam = pam transaction failed
|
||||
err_pam_abort = pam transaction aborted
|
||||
err_pam_acct_expired = account expired
|
||||
@@ -37,28 +29,17 @@ err_perm_dir = failed to change current directory
|
||||
err_perm_group = failed to downgrade group permissions
|
||||
err_perm_user = failed to downgrade user permissions
|
||||
err_pwnam = failed to get user info
|
||||
err_sleep = failed to execute sleep command
|
||||
err_tty_ctrl = tty control transfer failed
|
||||
err_unknown = an unknown error occurred
|
||||
err_user_gid = failed to set user GID
|
||||
err_user_init = failed to initialize user
|
||||
err_user_uid = failed to set user UID
|
||||
err_xauth = xauth command failed
|
||||
err_xcb_conn = xcb connection failed
|
||||
err_xsessions_dir = failed to find sessions folder
|
||||
err_xsessions_open = failed to open sessions folder
|
||||
insert = insert
|
||||
login = login
|
||||
f1 = F1 shutdown
|
||||
f2 = F2 reboot
|
||||
login = login:
|
||||
logout = logged out
|
||||
no_x11_support = x11 support disabled at compile-time
|
||||
normal = normal
|
||||
numlock = numlock
|
||||
other = other
|
||||
password = password
|
||||
restart = reboot
|
||||
password = password:
|
||||
shell = shell
|
||||
shutdown = shutdown
|
||||
sleep = sleep
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
authenticating = autenticando...
|
||||
brightness_down = bajar brillo
|
||||
brightness_up = subir brillo
|
||||
capslock = Bloq Mayús
|
||||
err_alloc = asignación de memoria fallida
|
||||
err_bounds = índice fuera de límites
|
||||
|
||||
err_chdir = error al abrir la carpeta home
|
||||
|
||||
err_console_dev = error al acceder a la consola
|
||||
err_dgn_oob = mensaje de registro
|
||||
err_domain = dominio inválido
|
||||
|
||||
|
||||
err_hostname = error al obtener el nombre de host
|
||||
err_mlock = error al bloquear la contraseña de memoria
|
||||
err_null = puntero nulo
|
||||
|
||||
err_pam = error en la transacción pam
|
||||
err_pam_abort = transacción pam abortada
|
||||
err_pam_acct_expired = cuenta expirada
|
||||
@@ -23,7 +15,7 @@ err_pam_auth = error de autenticación
|
||||
err_pam_authinfo_unavail = error al obtener información del usuario
|
||||
err_pam_authok_reqd = token expirado
|
||||
err_pam_buf = error de la memoria intermedia
|
||||
err_pam_cred_err = error al establecer las credenciales
|
||||
err_pam_cred_err = error al establecer la credenciales
|
||||
err_pam_cred_expired = credenciales expiradas
|
||||
err_pam_cred_insufficient = credenciales insuficientes
|
||||
err_pam_cred_unavail = error al obtener credenciales
|
||||
@@ -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_user = error al degradar los permisos del usuario
|
||||
err_pwnam = error al obtener la información del usuario
|
||||
|
||||
|
||||
|
||||
err_user_gid = error al establecer el GID del usuario
|
||||
err_user_init = error al inicializar usuario
|
||||
err_user_init = errpr al inicializar usuario
|
||||
err_user_uid = error al establecer el UID del usuario
|
||||
|
||||
|
||||
err_xsessions_dir = error al buscar la carpeta de sesiones
|
||||
err_xsessions_open = error al abrir la carpeta de sesiones
|
||||
insert = insertar
|
||||
login = usuario
|
||||
logout = cerrar sesión
|
||||
no_x11_support = soporte para x11 deshabilitado en tiempo de compilación
|
||||
normal = normal
|
||||
f1 = F1 apagar
|
||||
f2 = F2 reiniciar
|
||||
login = iniciar sesion:
|
||||
logout = cerrar sesion
|
||||
numlock = Bloq Num
|
||||
other = otro
|
||||
password = contraseña
|
||||
restart = reiniciar
|
||||
password = contraseña:
|
||||
shell = shell
|
||||
shutdown = apagar
|
||||
sleep = suspender
|
||||
wayland = wayland
|
||||
|
||||
xinitrc = xinitrc
|
||||
|
||||
@@ -1,27 +1,19 @@
|
||||
authenticating = authentification...
|
||||
brightness_down = diminuer la luminosité
|
||||
brightness_up = augmenter la luminosité
|
||||
capslock = verr.maj
|
||||
capslock = verr.maj
|
||||
err_alloc = échec d'allocation mémoire
|
||||
err_bounds = indice hors-limite
|
||||
err_brightness_change = échec du changement de luminosité
|
||||
err_chdir = échec de l'ouverture du répertoire home
|
||||
err_config = échec de lecture du fichier de configuration
|
||||
err_console_dev = échec d'accès à la console
|
||||
err_dgn_oob = message
|
||||
err_domain = domaine invalide
|
||||
err_empty_password = mot de passe vide non autorisé
|
||||
err_envlist = échec de lecture de la liste d'environnement
|
||||
err_hostname = échec de lecture du nom d'hôte
|
||||
err_hostname = échec de captation du nom d'hôte
|
||||
err_mlock = échec du verrouillage mémoire
|
||||
err_null = pointeur null
|
||||
err_numlock = échec de modification du verr.num
|
||||
err_pam = échec de la transaction pam
|
||||
err_pam_abort = transaction pam avortée
|
||||
err_pam_acct_expired = compte expiré
|
||||
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_authinfo_unavail = échec de l'obtention des infos utilisateur
|
||||
err_pam_buf = erreur de mémoire tampon
|
||||
err_pam_cred_err = échec de la modification des identifiants
|
||||
err_pam_cred_expired = identifiants expirés
|
||||
@@ -36,29 +28,18 @@ err_path = échec de la modification du path
|
||||
err_perm_dir = échec de changement de répertoire
|
||||
err_perm_group = échec du déclassement des permissions de groupe
|
||||
err_perm_user = échec du déclassement des permissions utilisateur
|
||||
err_pwnam = échec de lecture des infos utilisateur
|
||||
err_sleep = échec de l'exécution de la commande de veille
|
||||
err_tty_ctrl = échec du transfert de contrôle du terminal
|
||||
err_unknown = une erreur inconnue est survenue
|
||||
err_pwnam = échec de captation des infos utilisateur
|
||||
err_user_gid = échec de modification du GID
|
||||
err_user_init = échec d'initialisation de l'utilisateur
|
||||
err_user_uid = échec de modification du UID
|
||||
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_open = échec de l'ouverture du dossier de sessions
|
||||
insert = insertion
|
||||
login = identifiant
|
||||
logout = déconnecté
|
||||
no_x11_support = support pour x11 désactivé lors de la compilation
|
||||
normal = normal
|
||||
f1 = F1 éteindre
|
||||
f2 = F2 redémarrer
|
||||
login = identifiant :
|
||||
logout = déconnection
|
||||
numlock = verr.num
|
||||
other = autre
|
||||
password = mot de passe
|
||||
restart = redémarrer
|
||||
password = mot de passe :
|
||||
shell = shell
|
||||
shutdown = éteindre
|
||||
sleep = veille
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
|
||||
|
||||
capslock = capslock
|
||||
err_alloc = impossibile allocare memoria
|
||||
err_bounds = indice fuori limite
|
||||
|
||||
err_chdir = impossibile aprire home directory
|
||||
|
||||
err_console_dev = impossibile aprire console
|
||||
err_dgn_oob = messaggio log
|
||||
err_domain = dominio non valido
|
||||
|
||||
|
||||
err_hostname = impossibile ottenere hostname
|
||||
err_mlock = impossibile ottenere lock per la password in memoria
|
||||
err_null = puntatore nullo
|
||||
|
||||
err_pam = transazione PAM fallita
|
||||
err_pam_abort = transazione PAM interrotta
|
||||
err_pam_acct_expired = account scaduto
|
||||
err_pam_auth = errore di autenticazione
|
||||
err_pam_authinfo_unavail = impossibile ottenere informazioni utente
|
||||
err_pam_authok_reqd = token scaduto
|
||||
err_pam_buf = errore buffer memoria
|
||||
err_pam_cred_err = impossibile impostare credenziali
|
||||
err_pam_cred_expired = credenziali scadute
|
||||
err_pam_cred_insufficient = credenziali insufficienti
|
||||
err_pam_cred_unavail = impossibile ottenere credenziali
|
||||
err_pam_maxtries = raggiunto limite tentativi
|
||||
err_pam_perm_denied = permesso negato
|
||||
err_pam_session = errore di sessione
|
||||
err_pam_sys = errore di sistema
|
||||
err_pam_user_unknown = utente sconosciuto
|
||||
err_path = impossibile impostare percorso
|
||||
err_perm_dir = impossibile cambiare directory corrente
|
||||
err_perm_group = impossibile ridurre permessi gruppo
|
||||
err_perm_user = impossibile ridurre permessi utente
|
||||
err_pwnam = impossibile ottenere dati utente
|
||||
|
||||
|
||||
|
||||
err_user_gid = impossibile impostare GID utente
|
||||
err_user_init = impossibile inizializzare utente
|
||||
err_user_uid = impossible impostare UID utente
|
||||
|
||||
|
||||
err_xsessions_dir = impossibile localizzare cartella sessioni
|
||||
err_xsessions_open = impossibile aprire cartella sessioni
|
||||
|
||||
login = username
|
||||
logout = scollegato
|
||||
|
||||
|
||||
numlock = numlock
|
||||
|
||||
password = password
|
||||
restart = riavvio
|
||||
shell = shell
|
||||
shutdown = arresto
|
||||
|
||||
wayland = wayland
|
||||
|
||||
xinitrc = xinitrc
|
||||
@@ -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()
|
||||
@@ -1,64 +0,0 @@
|
||||
authenticating = uwierzytelnianie...
|
||||
brightness_down = zmniejsz jasność
|
||||
brightness_up = zwiększ jasność
|
||||
capslock = capslock
|
||||
err_alloc = nieudana alokacja pamięci
|
||||
err_bounds = indeks poza zakresem
|
||||
err_brightness_change = nie udało się zmienić jasności
|
||||
err_chdir = nie udało się otworzyć folderu domowego
|
||||
err_config = nie można przetworzyć pliku konfiguracyjnego
|
||||
err_console_dev = nie udało się uzyskać dostępu do konsoli
|
||||
err_dgn_oob = wiadomość loga
|
||||
err_domain = niepoprawna domena
|
||||
err_empty_password = puste hasło jest niedozwolone
|
||||
err_envlist = nie udało się pobrać listy zmiennych środowiskowych
|
||||
err_hostname = nie udało się uzyskać nazwy hosta
|
||||
err_mlock = nie udało się zablokować pamięci haseł
|
||||
err_null = pusty wskaźnik
|
||||
err_numlock = nie udało się ustawić numlock
|
||||
err_pam = transakcja pam nieudana
|
||||
err_pam_abort = transakcja pam przerwana
|
||||
err_pam_acct_expired = konto wygasło
|
||||
err_pam_auth = błąd uwierzytelniania
|
||||
err_pam_authinfo_unavail = nie udało się zdobyć informacji o użytkowniku
|
||||
err_pam_authok_reqd = token wygasł
|
||||
err_pam_buf = błąd bufora pamięci
|
||||
err_pam_cred_err = nie udało się ustawić uwierzytelnienia
|
||||
err_pam_cred_expired = uwierzytelnienie wygasło
|
||||
err_pam_cred_insufficient = niewystarczające uwierzytelnienie
|
||||
err_pam_cred_unavail = nie udało się uzyskać uwierzytelnienia
|
||||
err_pam_maxtries = osiągnięto limit prób
|
||||
err_pam_perm_denied = odmowa dostępu
|
||||
err_pam_session = błąd sesji
|
||||
err_pam_sys = błąd systemu
|
||||
err_pam_user_unknown = nieznany użytkownik
|
||||
err_path = nie udało się ustawić ścieżki
|
||||
err_perm_dir = nie udało się zmienić obecnego katalogu
|
||||
err_perm_group = nie udało się obniżyć uprawnień grupy
|
||||
err_perm_user = nie udało się obniżyć uprawnień użytkownika
|
||||
err_pwnam = nie udało się uzyskać informacji o użytkowniku
|
||||
err_sleep = nie udało się wykonać polecenia sleep
|
||||
err_tty_ctrl = nie udało się przekazać kontroli tty
|
||||
err_unknown = wystąpił nieznany błąd
|
||||
err_user_gid = nie udało się ustawić GID użytkownika
|
||||
err_user_init = nie udało się zainicjalizować użytkownika
|
||||
err_user_uid = nie udało się ustawić UID użytkownika
|
||||
err_xauth = polecenie xauth nie powiodło się
|
||||
err_xcb_conn = połączenie xcb nie powiodło się
|
||||
err_xsessions_dir = nie udało się znaleźć folderu sesji
|
||||
err_xsessions_open = nie udało się otworzyć folderu sesji
|
||||
insert = wstaw
|
||||
login = login
|
||||
logout = wylogowano
|
||||
no_x11_support = wsparcie X11 wyłączone podczas kompilacji
|
||||
normal = normalny
|
||||
numlock = numlock
|
||||
other = inny
|
||||
password = hasło
|
||||
restart = uruchom ponownie
|
||||
shell = powłoka
|
||||
shutdown = wyłącz
|
||||
sleep = uśpij
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
@@ -1,64 +1,45 @@
|
||||
|
||||
|
||||
|
||||
capslock = capslock
|
||||
err_alloc = erro na atribuição de memória
|
||||
capslock = caixa alta
|
||||
err_alloc = alocação de memória malsucedida
|
||||
err_bounds = índice fora de limites
|
||||
|
||||
err_chdir = erro ao abrir a pasta home
|
||||
|
||||
err_console_dev = erro ao aceder à consola
|
||||
err_dgn_oob = mensagem de registo
|
||||
err_chdir = não foi possível abrir o diretório home
|
||||
err_console_dev = não foi possível acessar o console
|
||||
err_dgn_oob = mensagem de log
|
||||
err_domain = domínio inválido
|
||||
|
||||
|
||||
err_hostname = erro ao obter o nome do host
|
||||
err_mlock = erro de bloqueio de memória
|
||||
err_hostname = não foi possível obter o nome do host
|
||||
err_mlock = bloqueio da memória de senha malsucedido
|
||||
err_null = ponteiro nulo
|
||||
|
||||
err_pam = erro na transação pam
|
||||
err_pam = transação pam malsucedida
|
||||
err_pam_abort = transação pam abortada
|
||||
err_pam_acct_expired = conta expirada
|
||||
err_pam_auth = erro de autenticação
|
||||
err_pam_authinfo_unavail = erro ao obter informação do utilizador
|
||||
err_pam_authok_reqd = token expirado
|
||||
err_pam_authinfo_unavail = não foi possível obter informações do usuário
|
||||
err_pam_authok_reqd = token expirada
|
||||
err_pam_buf = erro de buffer de memória
|
||||
err_pam_cred_err = erro ao definir credenciais
|
||||
err_pam_cred_err = erro para definir credenciais
|
||||
err_pam_cred_expired = credenciais expiradas
|
||||
err_pam_cred_insufficient = credenciais insuficientes
|
||||
err_pam_cred_unavail = erro ao obter credenciais
|
||||
err_pam_cred_unavail = não foi possível obter credenciais
|
||||
err_pam_maxtries = limite máximo de tentativas atingido
|
||||
err_pam_perm_denied = permissão negada
|
||||
err_pam_session = erro de sessão
|
||||
err_pam_sys = erro de sistema
|
||||
err_pam_user_unknown = utilizador desconhecido
|
||||
err_path = erro ao definir o caminho de acesso
|
||||
err_perm_dir = erro ao alterar o diretório atual
|
||||
err_perm_group = erro ao reduzir as permissões do grupo
|
||||
err_perm_user = erro ao reduzir as permissões do utilizador
|
||||
err_pwnam = erro ao obter informação do utilizador
|
||||
|
||||
|
||||
|
||||
err_user_gid = erro ao definir o GID do utilizador
|
||||
err_user_init = erro ao iniciar o utilizador
|
||||
err_user_uid = erro ao definir o UID do utilizador
|
||||
|
||||
|
||||
err_xsessions_dir = erro ao localizar a pasta das sessões
|
||||
err_xsessions_open = erro ao abrir a pasta das sessões
|
||||
|
||||
login = iniciar sessão
|
||||
logout = terminar sessão
|
||||
|
||||
|
||||
err_pam_user_unknown = usuário desconhecido
|
||||
err_path = não foi possível definir o caminho
|
||||
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_user = não foi possível reduzir as permissões de usuário
|
||||
err_pwnam = não foi possível obter informações do usuário
|
||||
err_user_gid = não foi possível definir o GID do usuário
|
||||
err_user_init = não foi possível iniciar o usuário
|
||||
err_user_uid = não foi possível definir o UID do usuário
|
||||
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
|
||||
f1 = F1 desligar
|
||||
f2 = F2 reiniciar
|
||||
login = conectar:
|
||||
logout = desconectado
|
||||
numlock = numlock
|
||||
|
||||
password = palavra-passe
|
||||
restart = reiniciar
|
||||
password = senha:
|
||||
shell = shell
|
||||
shutdown = encerrar
|
||||
|
||||
wayland = wayland
|
||||
|
||||
xinitrc = xinitrc
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
|
||||
|
||||
capslock = caixa alta
|
||||
err_alloc = alocação de memória malsucedida
|
||||
err_bounds = índice fora de limites
|
||||
|
||||
err_chdir = não foi possível abrir o diretório home
|
||||
|
||||
err_console_dev = não foi possível acessar o console
|
||||
err_dgn_oob = mensagem de log
|
||||
err_domain = domínio inválido
|
||||
|
||||
|
||||
err_hostname = não foi possível obter o nome do host
|
||||
err_mlock = bloqueio da memória de senha malsucedido
|
||||
err_null = ponteiro nulo
|
||||
|
||||
err_pam = transação pam malsucedida
|
||||
err_pam_abort = transação pam abortada
|
||||
err_pam_acct_expired = conta expirada
|
||||
err_pam_auth = erro de autenticação
|
||||
err_pam_authinfo_unavail = não foi possível obter informações do usuário
|
||||
err_pam_authok_reqd = token expirada
|
||||
err_pam_buf = erro de buffer de memória
|
||||
err_pam_cred_err = erro para definir credenciais
|
||||
err_pam_cred_expired = credenciais expiradas
|
||||
err_pam_cred_insufficient = credenciais insuficientes
|
||||
err_pam_cred_unavail = não foi possível obter credenciais
|
||||
err_pam_maxtries = limite máximo de tentativas atingido
|
||||
err_pam_perm_denied = permissão negada
|
||||
err_pam_session = erro de sessão
|
||||
err_pam_sys = erro de sistema
|
||||
err_pam_user_unknown = usuário desconhecido
|
||||
err_path = não foi possível definir o caminho
|
||||
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_user = não foi possível reduzir as permissões de usuário
|
||||
err_pwnam = não foi possível obter informações do usuário
|
||||
|
||||
|
||||
|
||||
err_user_gid = não foi possível definir o GID do usuário
|
||||
err_user_init = não foi possível iniciar o usuário
|
||||
err_user_uid = não foi possível definir o UID do usuário
|
||||
|
||||
|
||||
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
|
||||
|
||||
login = conectar
|
||||
logout = desconectado
|
||||
|
||||
|
||||
numlock = numlock
|
||||
|
||||
password = senha
|
||||
restart = reiniciar
|
||||
shell = shell
|
||||
shutdown = desligar
|
||||
|
||||
wayland = wayland
|
||||
|
||||
xinitrc = xinitrc
|
||||
@@ -1,12 +1,7 @@
|
||||
|
||||
|
||||
|
||||
capslock = capslock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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_acct_expired = cont expirat
|
||||
err_pam_auth = eroare de autentificare
|
||||
@@ -42,23 +34,12 @@ err_perm_user = nu s-a putut face downgrade permisiunilor de utilizator
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
login = utilizator
|
||||
f1 = F1 opreşte sistemul
|
||||
f2 = F2 resetează
|
||||
login = utilizator:
|
||||
logout = opreşte sesiunea
|
||||
|
||||
|
||||
numlock = numlock
|
||||
|
||||
password = parolă
|
||||
restart = resetează
|
||||
password = parolă:
|
||||
shell = shell
|
||||
shutdown = opreşte sistemul
|
||||
|
||||
wayland = wayland
|
||||
|
||||
xinitrc = xinitrc
|
||||
|
||||
@@ -1,34 +1,26 @@
|
||||
|
||||
|
||||
|
||||
capslock = capslock
|
||||
err_alloc = не удалось выделить память
|
||||
err_bounds = за пределами индекса
|
||||
|
||||
err_chdir = не удалось открыть домашнюю папку
|
||||
|
||||
err_console_dev = не удалось получить доступ к консоли
|
||||
err_dgn_oob = отладочное сообщение (log)
|
||||
err_domain = неверный домен
|
||||
|
||||
|
||||
err_hostname = не удалось получить имя хоста
|
||||
err_mlock = сбой блокировки памяти
|
||||
err_null = нулевой указатель
|
||||
|
||||
err_pam = pam транзакция не удалась
|
||||
err_pam_abort = pam транзакция прервана
|
||||
err_pam_acct_expired = срок действия аккаунта истёк
|
||||
err_pam_acct_expired = срок действия аккаунта истек
|
||||
err_pam_auth = ошибка аутентификации
|
||||
err_pam_authinfo_unavail = не удалось получить информацию о пользователе
|
||||
err_pam_authok_reqd = токен истёк
|
||||
err_pam_authok_reqd = токен истек
|
||||
err_pam_buf = ошибка буфера памяти
|
||||
err_pam_cred_err = не удалось установить полномочия
|
||||
err_pam_cred_expired = полномочия истекли
|
||||
err_pam_cred_insufficient = недостаточно полномочий
|
||||
err_pam_cred_insufficient = недостаточо полномочий
|
||||
err_pam_cred_unavail = не удалось получить полномочия
|
||||
err_pam_maxtries = лимит попыток исчерпан
|
||||
err_pam_perm_denied = доступ запрещён
|
||||
err_pam_perm_denied = доступ запрещен
|
||||
err_pam_session = ошибка сессии
|
||||
err_pam_sys = системная ошибка
|
||||
err_pam_user_unknown = неизвестный пользователь
|
||||
@@ -37,28 +29,17 @@ 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 = логин
|
||||
f1 = F1 выключить
|
||||
f2 = F2 перезагрузить
|
||||
login = логин:
|
||||
logout = logged out
|
||||
|
||||
|
||||
numlock = numlock
|
||||
|
||||
password = пароль
|
||||
restart = перезагрузить
|
||||
password = пароль:
|
||||
shell = shell
|
||||
shutdown = выключить
|
||||
|
||||
wayland = wayland
|
||||
|
||||
xinitrc = xinitrc
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
|
||||
|
||||
capslock = capslock
|
||||
err_alloc = neuspijesna alokacija memorije
|
||||
err_bounds = izvan granica indeksa
|
||||
|
||||
err_chdir = neuspijesno otvaranje home foldera
|
||||
|
||||
err_console_dev = neuspijesno pristupanje konzoli
|
||||
err_dgn_oob = log poruka
|
||||
err_domain = nevazeci domen
|
||||
|
||||
|
||||
err_hostname = neuspijesno trazenje hostname-a
|
||||
err_mlock = neuspijesno zakljucavanje memorije lozinke
|
||||
err_null = null pokazivac
|
||||
|
||||
err_pam = pam transakcija neuspijesna
|
||||
err_pam_abort = pam transakcija prekinuta
|
||||
err_pam_acct_expired = nalog istekao
|
||||
err_pam_auth = greska pri autentikaciji
|
||||
err_pam_authinfo_unavail = neuspjelo uzimanje informacija o korisniku
|
||||
err_pam_authok_reqd = token istekao
|
||||
err_pam_buf = greska bafera memorije
|
||||
err_pam_cred_err = neuspjelo postavljanje kredencijala
|
||||
err_pam_cred_expired = kredencijali istekli
|
||||
err_pam_cred_insufficient = nedovoljni kredencijali
|
||||
err_pam_cred_unavail = neuspjelo uzimanje kredencijala
|
||||
err_pam_maxtries = dostignut maksimalan broj pokusaja
|
||||
err_pam_perm_denied = nedozovoljeno
|
||||
err_pam_session = greska sesije
|
||||
err_pam_sys = greska sistema
|
||||
err_pam_user_unknown = nepoznat korisnik
|
||||
err_path = neuspjelo postavljanje path-a
|
||||
err_perm_dir = neuspjelo mijenjanje foldera
|
||||
err_perm_group = neuspjesno snizavanje dozvola grupe
|
||||
err_perm_user = neuspijesno snizavanje dozvola korisnika
|
||||
err_pwnam = neuspijesno skupljanje informacija o korisniku
|
||||
|
||||
|
||||
|
||||
err_user_gid = neuspijesno postavljanje korisničkog GID-a
|
||||
err_user_init = neuspijensa inicijalizacija korisnika
|
||||
err_user_uid = neuspijesno postavljanje UID-a korisnika
|
||||
|
||||
|
||||
err_xsessions_dir = neuspijesno pronalazenje foldera sesija
|
||||
err_xsessions_open = neuspijesno otvaranje foldera sesija
|
||||
|
||||
login = korisnik
|
||||
logout = izlogovan
|
||||
|
||||
|
||||
numlock = numlock
|
||||
|
||||
password = lozinka
|
||||
restart = ponovo pokreni
|
||||
shell = shell
|
||||
shutdown = ugasi
|
||||
|
||||
wayland = wayland
|
||||
|
||||
xinitrc = xinitrc
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
|
||||
|
||||
capslock = capslock
|
||||
err_alloc = misslyckad minnesallokering
|
||||
err_bounds = utanför banan index
|
||||
|
||||
err_chdir = misslyckades att öppna hemkatalog
|
||||
|
||||
err_console_dev = misslyckades att komma åt konsol
|
||||
err_dgn_oob = loggmeddelande
|
||||
err_domain = okänd domän
|
||||
|
||||
|
||||
err_hostname = misslyckades att hämta värdnamn
|
||||
err_mlock = misslyckades att låsa lösenordsminne
|
||||
err_null = nullpekare
|
||||
|
||||
err_pam = pam-transaktion misslyckades
|
||||
err_pam_abort = pam-transaktion avbröts
|
||||
err_pam_acct_expired = konto upphört
|
||||
err_pam_auth = autentiseringsfel
|
||||
err_pam_authinfo_unavail = misslyckades att hämta användarinfo
|
||||
err_pam_authok_reqd = token utgången
|
||||
err_pam_buf = minnesbuffer fel
|
||||
err_pam_cred_err = misslyckades att ställa in inloggningsuppgifter
|
||||
err_pam_cred_expired = inloggningsuppgifter upphörda
|
||||
err_pam_cred_insufficient = otillräckliga inloggningsuppgifter
|
||||
err_pam_cred_unavail = misslyckades att hämta inloggningsuppgifter
|
||||
err_pam_maxtries = nådde maximal försöksgräns
|
||||
err_pam_perm_denied = åtkomst nekad
|
||||
err_pam_session = sessionsfel
|
||||
err_pam_sys = systemfel
|
||||
err_pam_user_unknown = okänd användare
|
||||
err_path = misslyckades att ställa in sökväg
|
||||
err_perm_dir = misslyckades att ändra aktuell katalog
|
||||
err_perm_group = misslyckades att nergradera gruppbehörigheter
|
||||
err_perm_user = misslyckades att nergradera användarbehörigheter
|
||||
err_pwnam = misslyckades att hämta användarinfo
|
||||
|
||||
|
||||
|
||||
err_user_gid = misslyckades att ställa in användar-GID
|
||||
err_user_init = misslyckades att initialisera användaren
|
||||
err_user_uid = misslyckades att ställa in användar-UID
|
||||
|
||||
|
||||
err_xsessions_dir = misslyckades att hitta sessionskatalog
|
||||
err_xsessions_open = misslyckades att öppna sessionskatalog
|
||||
|
||||
login = inloggning
|
||||
logout = utloggad
|
||||
|
||||
|
||||
numlock = numlock
|
||||
|
||||
password = lösenord
|
||||
restart = starta om
|
||||
shell = skal
|
||||
shutdown = stäng av
|
||||
|
||||
wayland = wayland
|
||||
|
||||
xinitrc = xinitrc
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
|
||||
|
||||
capslock = capslock
|
||||
err_alloc = basarisiz bellek ayirma
|
||||
err_bounds = sinirlarin disinda dizin
|
||||
|
||||
err_chdir = ev klasoru acilamadi
|
||||
|
||||
err_console_dev = konsola erisilemedi
|
||||
err_dgn_oob = log mesaji
|
||||
err_domain = gecersiz etki alani
|
||||
|
||||
|
||||
err_hostname = ana bilgisayar adi alinamadi
|
||||
err_mlock = parola bellegi kilitlenemedi
|
||||
err_null = bos isaretci hatasi
|
||||
|
||||
err_pam = pam islemi basarisiz oldu
|
||||
err_pam_abort = pam islemi durduruldu
|
||||
err_pam_acct_expired = hesabin suresi dolmus
|
||||
err_pam_auth = kimlik dogrulama hatasi
|
||||
err_pam_authinfo_unavail = kullanici bilgileri getirilirken hata olustu
|
||||
err_pam_authok_reqd = suresi dolmus token
|
||||
err_pam_buf = bellek arabellegi hatasi
|
||||
err_pam_cred_err = kimlik bilgileri ayarlanamadi
|
||||
err_pam_cred_expired = kimlik bilgilerinin suresi dolmus
|
||||
err_pam_cred_insufficient = yetersiz kimlik bilgileri
|
||||
err_pam_cred_unavail = kimlik bilgileri alinamadi
|
||||
err_pam_maxtries = en fazla deneme sinirina ulasildi
|
||||
err_pam_perm_denied = izin reddedildi
|
||||
err_pam_session = oturum hatasi
|
||||
err_pam_sys = sistem hatasi
|
||||
err_pam_user_unknown = bilinmeyen kullanici
|
||||
err_path = yol ayarlanamadi
|
||||
err_perm_dir = gecerli dizin degistirilemedi
|
||||
err_perm_group = grup izinleri dusurulemedi
|
||||
err_perm_user = kullanici izinleri dusurulemedi
|
||||
err_pwnam = kullanici bilgileri alinamadi
|
||||
|
||||
|
||||
|
||||
err_user_gid = kullanici icin GID ayarlanamadi
|
||||
err_user_init = kullanici oturumu baslatilamadi
|
||||
err_user_uid = kullanici icin UID ayarlanamadi
|
||||
|
||||
|
||||
err_xsessions_dir = oturumlar klasoru bulunamadi
|
||||
err_xsessions_open = oturumlar klasoru acilamadi
|
||||
|
||||
login = kullanici
|
||||
logout = oturumdan cikis yapildi
|
||||
|
||||
|
||||
numlock = numlock
|
||||
|
||||
password = sifre
|
||||
restart = yeniden baslat
|
||||
shell = shell
|
||||
shutdown = makineyi kapat
|
||||
|
||||
wayland = wayland
|
||||
|
||||
xinitrc = xinitrc
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
|
||||
|
||||
capslock = capslock
|
||||
err_alloc = невдале виділення пам'яті
|
||||
err_bounds = поза межами індексу
|
||||
|
||||
err_chdir = не вдалося відкрити домашній каталог
|
||||
|
||||
err_console_dev = невдалий доступ до консолі
|
||||
err_dgn_oob = повідомлення журналу (log)
|
||||
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 = numlock
|
||||
|
||||
password = пароль
|
||||
restart = перезавантажити
|
||||
shell = оболонка
|
||||
shutdown = вимкнути
|
||||
|
||||
wayland = wayland
|
||||
|
||||
xinitrc = xinitrc
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
name="ly"
|
||||
description="TUI Display Manager"
|
||||
|
||||
## Supervisor daemon
|
||||
supervisor=supervise-daemon
|
||||
respawn_period=60
|
||||
pidfile=/run/"${RC_SVCNAME}.pid"
|
||||
|
||||
## Check for getty or agetty
|
||||
if [ -x /sbin/getty ] || [ -x /bin/getty ];
|
||||
then
|
||||
# busybox
|
||||
commandB="/sbin/getty"
|
||||
elif [ -x /sbin/agetty ] || [ -x /bin/agetty ];
|
||||
then
|
||||
# util-linux
|
||||
commandUL="/sbin/agetty"
|
||||
fi
|
||||
|
||||
## Get the tty from the conf file
|
||||
CONFTTY=$(cat $CONFIG_DIRECTORY/ly/config.ini | sed -n 's/^tty.*=[^1-9]*// p')
|
||||
|
||||
## The execution vars
|
||||
# If CONFTTY is empty then default to $DEFAULT_TTY
|
||||
TTY="tty${CONFTTY:-$DEFAULT_TTY}"
|
||||
TERM=linux
|
||||
BAUD=38400
|
||||
# If we don't have getty then we should have agetty
|
||||
command=${commandB:-$commandUL}
|
||||
command_args_foreground="-nl $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME $TTY $BAUD $TERM"
|
||||
|
||||
depend() {
|
||||
after agetty
|
||||
provide display-manager
|
||||
want elogind
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
if [ -x /sbin/agetty -o -x /bin/agetty ]; then
|
||||
# util-linux specific settings
|
||||
if [ "${tty}" = "tty1" ]; then
|
||||
GETTY_ARGS="--noclear"
|
||||
fi
|
||||
fi
|
||||
|
||||
BAUD_RATE=38400
|
||||
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')
|
||||
TTY=tty${auxtty:-$DEFAULT_TTY}
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
[ -r conf ] && . ./conf
|
||||
|
||||
exec utmpset -w ${TTY}
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ -r conf ] && . ./conf
|
||||
|
||||
if [ -x /sbin/getty -o -x /bin/getty ]; then
|
||||
# busybox
|
||||
GETTY=getty
|
||||
elif [ -x /sbin/agetty -o -x /bin/agetty ]; then
|
||||
# util-linux
|
||||
GETTY=agetty
|
||||
fi
|
||||
|
||||
exec setsid ${GETTY} ${GETTY_ARGS} -nl $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME "${TTY}" "${BAUD_RATE}" "${TERM_NAME}"
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/execlineb -P
|
||||
exec agetty -L -8 -n -l $PREFIX_DIRECTORY/bin/$EXE_NAME tty$DEFAULT_TTY 115200
|
||||
@@ -1 +0,0 @@
|
||||
longrun
|
||||
@@ -1,14 +1,13 @@
|
||||
[Unit]
|
||||
Description=TUI display manager
|
||||
After=systemd-user-sessions.service plymouth-quit-wait.service
|
||||
After=getty@tty$DEFAULT_TTY.service
|
||||
Conflicts=getty@tty$DEFAULT_TTY.service
|
||||
After=getty@tty2.service
|
||||
|
||||
[Service]
|
||||
Type=idle
|
||||
ExecStart=$PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME
|
||||
ExecStart=/usr/bin/ly
|
||||
StandardInput=tty
|
||||
TTYPath=/dev/tty$DEFAULT_TTY
|
||||
TTYPath=/dev/tty2
|
||||
TTYReset=yes
|
||||
TTYVHangup=yes
|
||||
|
||||
|
||||
10
res/pam.d/ly
10
res/pam.d/ly
@@ -1,16 +1,6 @@
|
||||
#%PAM-1.0
|
||||
|
||||
auth include login
|
||||
-auth optional pam_gnome_keyring.so
|
||||
-auth optional pam_kwallet5.so
|
||||
|
||||
account include login
|
||||
|
||||
password include login
|
||||
-password optional pam_gnome_keyring.so use_authtok
|
||||
|
||||
-session optional pam_systemd.so class=greeter
|
||||
-session optional pam_elogind.so
|
||||
session include login
|
||||
-session optional pam_gnome_keyring.so auto_start
|
||||
-session optional pam_kwallet5.so auto_start
|
||||
|
||||
107
res/setup.sh
107
res/setup.sh
@@ -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
31
res/valgrind.supp
Normal 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
|
||||
...
|
||||
}
|
||||
54
res/wsetup.sh
Executable file
54
res/wsetup.sh
Executable file
@@ -0,0 +1,54 @@
|
||||
#!/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
|
||||
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 $@
|
||||
102
res/xsetup.sh
Executable file
102
res/xsetup.sh
Executable file
@@ -0,0 +1,102 @@
|
||||
#! /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
|
||||
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 [ -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
|
||||
|
||||
if [ -f "$USERXSESSION" ]; then
|
||||
. "$USERXSESSION"
|
||||
fi
|
||||
|
||||
if [ -z "$*" ]; then
|
||||
exec xmessage -center -buttons OK:0 -default OK "Sorry, $DESKTOP_SESSION is no valid session."
|
||||
else
|
||||
exec $@
|
||||
fi
|
||||
1
selinux/ly.fc
Normal file
1
selinux/ly.fc
Normal file
@@ -0,0 +1 @@
|
||||
/usr/bin/ly -- gen_context(system_u:object_r:ly_exec_t,s0)
|
||||
41
selinux/ly.if
Normal file
41
selinux/ly.if
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
## <summary>policy for ly</summary>
|
||||
|
||||
########################################
|
||||
## <summary>
|
||||
## Execute ly_exec_t in the ly domain.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed to transition.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`ly_domtrans',`
|
||||
gen_require(`
|
||||
type ly_t, ly_exec_t;
|
||||
')
|
||||
|
||||
corecmd_search_bin($1)
|
||||
domtrans_pattern($1, ly_exec_t, ly_t)
|
||||
')
|
||||
|
||||
######################################
|
||||
## <summary>
|
||||
## Execute ly in the caller domain.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
## Domain allowed access.
|
||||
## </summary>
|
||||
## </param>
|
||||
#
|
||||
interface(`ly_exec',`
|
||||
gen_require(`
|
||||
type ly_exec_t;
|
||||
')
|
||||
|
||||
corecmd_search_bin($1)
|
||||
can_exec($1, ly_exec_t)
|
||||
')
|
||||
|
||||
32
selinux/ly.te
Executable file
32
selinux/ly.te
Executable file
@@ -0,0 +1,32 @@
|
||||
policy_module(ly, 1.0.0)
|
||||
|
||||
########################################
|
||||
#
|
||||
# Declarations
|
||||
#
|
||||
|
||||
type ly_t;
|
||||
type ly_exec_t;
|
||||
init_daemon_domain(ly_t, ly_exec_t)
|
||||
|
||||
permissive ly_t;
|
||||
|
||||
########################################
|
||||
#
|
||||
# ly local policy
|
||||
#
|
||||
allow ly_t self:capability { setgid setuid };
|
||||
allow ly_t self:process { fork signal_perms };
|
||||
allow ly_t self:process transition;
|
||||
allow ly_t self:fifo_file rw_fifo_file_perms;
|
||||
allow ly_t self:unix_stream_socket create_stream_socket_perms;
|
||||
|
||||
domain_use_interactive_fds(ly_t)
|
||||
|
||||
files_read_etc_files(ly_t)
|
||||
|
||||
auth_use_nsswitch(ly_t)
|
||||
|
||||
logging_send_audit_msgs(ly_t)
|
||||
|
||||
miscfiles_read_localization(ly_t)
|
||||
@@ -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,
|
||||
@@ -1,39 +0,0 @@
|
||||
const std = @import("std");
|
||||
|
||||
const ErrInt = std.meta.Int(.unsigned, @bitSizeOf(anyerror));
|
||||
|
||||
const ErrorHandler = packed struct {
|
||||
has_error: bool = false,
|
||||
err_int: ErrInt = 0,
|
||||
};
|
||||
|
||||
const SharedError = @This();
|
||||
|
||||
data: []align(std.heap.page_size_min) u8,
|
||||
|
||||
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);
|
||||
|
||||
return .{ .data = data };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *SharedError) void {
|
||||
std.posix.munmap(self.data);
|
||||
}
|
||||
|
||||
pub fn writeError(self: SharedError, err: anyerror) void {
|
||||
var buf_stream = std.io.fixedBufferStream(self.data);
|
||||
const writer = buf_stream.writer();
|
||||
writer.writeStruct(ErrorHandler{ .has_error = true, .err_int = @intFromError(err) }) catch {};
|
||||
}
|
||||
|
||||
pub fn readError(self: SharedError) ?anyerror {
|
||||
var buf_stream = std.io.fixedBufferStream(self.data);
|
||||
const reader = buf_stream.reader();
|
||||
const err_handler = try reader.readStruct(ErrorHandler);
|
||||
|
||||
if (err_handler.has_error)
|
||||
return @errorFromInt(err_handler.err_int);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Animation = @import("../tui/Animation.zig");
|
||||
const Cell = @import("../tui/Cell.zig");
|
||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
||||
|
||||
const Doom = @This();
|
||||
|
||||
pub const STEPS = 12;
|
||||
|
||||
allocator: Allocator,
|
||||
terminal_buffer: *TerminalBuffer,
|
||||
buffer: []u8,
|
||||
fire: [STEPS + 1]Cell,
|
||||
|
||||
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, top_color: u32, middle_color: u32, bottom_color: u32) !Doom {
|
||||
const buffer = try allocator.alloc(u8, terminal_buffer.width * terminal_buffer.height);
|
||||
initBuffer(buffer, terminal_buffer.width);
|
||||
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.terminal_buffer = terminal_buffer,
|
||||
.buffer = buffer,
|
||||
.fire = [_]Cell{
|
||||
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 {
|
||||
return Animation.init(self, deinit, realloc, draw);
|
||||
}
|
||||
|
||||
fn deinit(self: *Doom) void {
|
||||
self.allocator.free(self.buffer);
|
||||
}
|
||||
|
||||
fn realloc(self: *Doom) anyerror!void {
|
||||
const buffer = try self.allocator.realloc(self.buffer, self.terminal_buffer.width * self.terminal_buffer.height);
|
||||
initBuffer(buffer, self.terminal_buffer.width);
|
||||
self.buffer = buffer;
|
||||
}
|
||||
|
||||
fn draw(self: *Doom) void {
|
||||
for (0..self.terminal_buffer.width) |x| {
|
||||
// We start from 1 so that we always have the topmost line when spreading fire
|
||||
for (1..self.terminal_buffer.height) |y| {
|
||||
// Get current cell
|
||||
const from = y * self.terminal_buffer.width + x;
|
||||
const cell_index = self.buffer[from];
|
||||
|
||||
// Spread fire
|
||||
const propagate = self.terminal_buffer.random.int(u1);
|
||||
const to = from - self.terminal_buffer.width; // Get the line above
|
||||
|
||||
self.buffer[to] = if (cell_index > 0) cell_index - propagate else cell_index;
|
||||
|
||||
// Put the cell
|
||||
const cell = self.fire[cell_index];
|
||||
cell.put(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn initBuffer(buffer: []u8, width: usize) void {
|
||||
const length = buffer.len - width;
|
||||
const slice_start = buffer[0..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_end, STEPS);
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -1,188 +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 Allocator = std.mem.Allocator;
|
||||
const Random = std.Random;
|
||||
|
||||
pub const FRAME_DELAY: usize = 8;
|
||||
|
||||
// Characters change mid-scroll
|
||||
pub const MID_SCROLL_CHANGE = true;
|
||||
|
||||
const DOT_HEAD_COLOR: u32 = @intCast(TerminalBuffer.Color.WHITE | TerminalBuffer.Styling.BOLD);
|
||||
|
||||
const Matrix = @This();
|
||||
|
||||
pub const Dot = struct {
|
||||
value: ?usize,
|
||||
is_head: bool,
|
||||
};
|
||||
|
||||
pub const Line = struct {
|
||||
space: usize,
|
||||
length: usize,
|
||||
update: usize,
|
||||
};
|
||||
|
||||
allocator: Allocator,
|
||||
terminal_buffer: *TerminalBuffer,
|
||||
dots: []Dot,
|
||||
lines: []Line,
|
||||
frame: usize,
|
||||
count: usize,
|
||||
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 {
|
||||
const dots = try allocator.alloc(Dot, terminal_buffer.width * (terminal_buffer.height + 1));
|
||||
const lines = try allocator.alloc(Line, terminal_buffer.width);
|
||||
|
||||
initBuffers(dots, lines, terminal_buffer.width, terminal_buffer.height, terminal_buffer.random);
|
||||
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.terminal_buffer = terminal_buffer,
|
||||
.dots = dots,
|
||||
.lines = lines,
|
||||
.frame = 3,
|
||||
.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 {
|
||||
return Animation.init(self, deinit, realloc, draw);
|
||||
}
|
||||
|
||||
fn deinit(self: *Matrix) void {
|
||||
self.allocator.free(self.dots);
|
||||
self.allocator.free(self.lines);
|
||||
}
|
||||
|
||||
fn realloc(self: *Matrix) anyerror!void {
|
||||
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);
|
||||
|
||||
initBuffers(dots, lines, self.terminal_buffer.width, self.terminal_buffer.height, self.terminal_buffer.random);
|
||||
|
||||
self.dots = dots;
|
||||
self.lines = lines;
|
||||
}
|
||||
|
||||
fn draw(self: *Matrix) void {
|
||||
const buf_height = self.terminal_buffer.height;
|
||||
const buf_width = self.terminal_buffer.width;
|
||||
self.count += 1;
|
||||
if (self.count > FRAME_DELAY) {
|
||||
self.frame += 1;
|
||||
if (self.frame > 4) self.frame = 1;
|
||||
self.count = 0;
|
||||
|
||||
var x: usize = 0;
|
||||
while (x < self.terminal_buffer.width) : (x += 2) {
|
||||
var tail: usize = 0;
|
||||
var line = &self.lines[x];
|
||||
if (self.frame <= line.update) continue;
|
||||
|
||||
if (self.dots[x].value == null and self.dots[self.terminal_buffer.width + x].value == ' ') {
|
||||
if (line.space > 0) {
|
||||
line.space -= 1;
|
||||
} else {
|
||||
const randint = self.terminal_buffer.random.int(u16);
|
||||
const h = self.terminal_buffer.height;
|
||||
line.length = @mod(randint, h - 3) + 3;
|
||||
self.dots[x].value = @mod(randint, self.max_codepoint) + self.min_codepoint;
|
||||
line.space = @mod(randint, h + 1);
|
||||
}
|
||||
}
|
||||
|
||||
var y: usize = 0;
|
||||
var first_col = true;
|
||||
var seg_len: u64 = 0;
|
||||
height_it: while (y <= buf_height) : (y += 1) {
|
||||
var dot = &self.dots[buf_width * y + x];
|
||||
// Skip over spaces
|
||||
while (y <= buf_height and (dot.value == ' ' or dot.value == null)) {
|
||||
y += 1;
|
||||
if (y > buf_height) break :height_it;
|
||||
dot = &self.dots[buf_width * y + x];
|
||||
}
|
||||
|
||||
// Find the head of this column
|
||||
tail = y;
|
||||
seg_len = 0;
|
||||
while (y <= buf_height and dot.value != ' ' and dot.value != null) {
|
||||
dot.is_head = false;
|
||||
if (MID_SCROLL_CHANGE) {
|
||||
const randint = self.terminal_buffer.random.int(u16);
|
||||
if (@mod(randint, 8) == 0) {
|
||||
dot.value = @mod(randint, self.max_codepoint) + self.min_codepoint;
|
||||
}
|
||||
}
|
||||
|
||||
y += 1;
|
||||
seg_len += 1;
|
||||
// Head's down offscreen
|
||||
if (y > buf_height) {
|
||||
self.dots[buf_width * tail + x].value = ' ';
|
||||
break :height_it;
|
||||
}
|
||||
dot = &self.dots[buf_width * y + x];
|
||||
}
|
||||
|
||||
const randint = self.terminal_buffer.random.int(u16);
|
||||
dot.value = @mod(randint, self.max_codepoint) + self.min_codepoint;
|
||||
dot.is_head = true;
|
||||
|
||||
if (seg_len > line.length or !first_col) {
|
||||
self.dots[buf_width * tail + x].value = ' ';
|
||||
self.dots[x].value = null;
|
||||
}
|
||||
first_col = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var x: usize = 0;
|
||||
while (x < buf_width) : (x += 2) {
|
||||
var y: usize = 1;
|
||||
while (y <= self.terminal_buffer.height) : (y += 1) {
|
||||
const dot = self.dots[buf_width * y + x];
|
||||
const cell = if (dot.value == null or dot.value == ' ') self.default_cell else Cell{
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn initBuffers(dots: []Dot, lines: []Line, width: usize, height: usize, random: Random) void {
|
||||
var y: usize = 0;
|
||||
while (y <= height) : (y += 1) {
|
||||
var x: usize = 0;
|
||||
while (x < width) : (x += 2) {
|
||||
dots[y * width + x].value = null;
|
||||
}
|
||||
}
|
||||
|
||||
var x: usize = 0;
|
||||
while (x < width) : (x += 2) {
|
||||
var line = lines[x];
|
||||
line.space = @mod(random.int(u16), height) + 1;
|
||||
line.length = @mod(random.int(u16), height - 3) + 3;
|
||||
line.update = @mod(random.int(u16), 3) + 1;
|
||||
lines[x] = line;
|
||||
|
||||
dots[width + x].value = ' ';
|
||||
}
|
||||
}
|
||||
542
src/auth.zig
542
src/auth.zig
@@ -1,542 +0,0 @@
|
||||
const std = @import("std");
|
||||
const build_options = @import("build_options");
|
||||
const builtin = @import("builtin");
|
||||
const enums = @import("enums.zig");
|
||||
const Environment = @import("Environment.zig");
|
||||
const interop = @import("interop.zig");
|
||||
const SharedError = @import("SharedError.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Md5 = std.crypto.hash.Md5;
|
||||
const utmp = interop.utmp;
|
||||
const Utmp = utmp.utmpx;
|
||||
|
||||
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;
|
||||
pub fn xorgSignalHandler(i: c_int) callconv(.C) void {
|
||||
if (xorg_pid > 0) _ = std.c.kill(xorg_pid, i);
|
||||
}
|
||||
|
||||
var child_pid: std.posix.pid_t = 0;
|
||||
pub fn sessionSignalHandler(i: c_int) callconv(.C) void {
|
||||
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 {
|
||||
var tty_buffer: [3]u8 = undefined;
|
||||
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{options.tty});
|
||||
|
||||
var pam_tty_buffer: [6]u8 = undefined;
|
||||
const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{options.tty});
|
||||
|
||||
// Set the XDG environment variables
|
||||
setXdgSessionEnv(current_environment.display_server);
|
||||
try setXdgEnv(tty_str, current_environment.xdg_session_desktop, current_environment.xdg_desktop_names);
|
||||
|
||||
// Open the PAM session
|
||||
var credentials = [_:null]?[*:0]const u8{ login, password };
|
||||
|
||||
const conv = interop.pam.pam_conv{
|
||||
.conv = loginConv,
|
||||
.appdata_ptr = @ptrCast(&credentials),
|
||||
};
|
||||
var handle: ?*interop.pam.pam_handle = undefined;
|
||||
|
||||
var status = interop.pam.pam_start(options.service_name, null, &conv, &handle);
|
||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||
defer _ = interop.pam.pam_end(handle, status);
|
||||
|
||||
// Set PAM_TTY as the current TTY. This is required in case it isn't being set by another PAM module
|
||||
status = interop.pam.pam_set_item(handle, interop.pam.PAM_TTY, pam_tty_str.ptr);
|
||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||
|
||||
// Do the PAM routine
|
||||
status = interop.pam.pam_authenticate(handle, 0);
|
||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||
|
||||
status = interop.pam.pam_acct_mgmt(handle, 0);
|
||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||
|
||||
status = interop.pam.pam_setcred(handle, interop.pam.PAM_ESTABLISH_CRED);
|
||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||
defer status = interop.pam.pam_setcred(handle, interop.pam.PAM_DELETE_CRED);
|
||||
|
||||
status = interop.pam.pam_open_session(handle, 0);
|
||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||
defer status = interop.pam.pam_close_session(handle, 0);
|
||||
|
||||
var pwd: *interop.pwd.passwd = undefined;
|
||||
{
|
||||
defer interop.pwd.endpwent();
|
||||
|
||||
// Get password structure from username
|
||||
pwd = interop.pwd.getpwnam(login) orelse return error.GetPasswordNameFailed;
|
||||
}
|
||||
|
||||
// Set user shell if it hasn't already been set
|
||||
if (pwd.pw_shell == null) {
|
||||
interop.unistd.setusershell();
|
||||
pwd.pw_shell = interop.unistd.getusershell();
|
||||
interop.unistd.endusershell();
|
||||
}
|
||||
|
||||
var shared_err = try SharedError.init();
|
||||
defer shared_err.deinit();
|
||||
|
||||
child_pid = try std.posix.fork();
|
||||
if (child_pid == 0) {
|
||||
startSession(options, tty_str, pwd, handle, current_environment) catch |e| {
|
||||
shared_err.writeError(e);
|
||||
std.process.exit(1);
|
||||
};
|
||||
std.process.exit(0);
|
||||
}
|
||||
|
||||
var entry = std.mem.zeroes(Utmp);
|
||||
|
||||
{
|
||||
// If an error occurs here, we can send SIGTERM to the session
|
||||
errdefer cleanup: {
|
||||
_ = std.posix.kill(child_pid, std.posix.SIG.TERM) catch break :cleanup;
|
||||
_ = std.posix.waitpid(child_pid, 0);
|
||||
}
|
||||
|
||||
// If we receive SIGTERM, forward it to child_pid
|
||||
const act = std.posix.Sigaction{
|
||||
.handler = .{ .handler = &sessionSignalHandler },
|
||||
.mask = std.posix.empty_sigset,
|
||||
.flags = 0,
|
||||
};
|
||||
std.posix.sigaction(std.posix.SIG.TERM, &act, null);
|
||||
|
||||
try addUtmpEntry(&entry, pwd.pw_name.?, child_pid);
|
||||
}
|
||||
// Wait for the session to stop
|
||||
_ = std.posix.waitpid(child_pid, 0);
|
||||
|
||||
removeUtmpEntry(&entry);
|
||||
|
||||
if (shared_err.readError()) |err| return err;
|
||||
}
|
||||
|
||||
fn startSession(
|
||||
options: AuthOptions,
|
||||
tty_str: [:0]u8,
|
||||
pwd: *interop.pwd.passwd,
|
||||
handle: ?*interop.pam.pam_handle,
|
||||
current_environment: Environment,
|
||||
) !void {
|
||||
if (builtin.os.tag == .freebsd) {
|
||||
// FreeBSD has initgroups() in unistd
|
||||
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()
|
||||
const result = interop.pwd.setusercontext(null, pwd, pwd.pw_uid, interop.pwd.LOGIN_SETALL);
|
||||
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
|
||||
try initEnv(pwd, options.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
|
||||
const pam_env_vars: ?[*:null]?[*:0]u8 = interop.pam.pam_getenvlist(handle);
|
||||
if (pam_env_vars == null) return error.GetEnvListFailed;
|
||||
|
||||
const env_list = std.mem.span(pam_env_vars.?);
|
||||
for (env_list) |env_var| _ = interop.stdlib.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
|
||||
switch (current_environment.display_server) {
|
||||
.wayland => try executeWaylandCmd(pwd.pw_shell.?, options, current_environment.cmd),
|
||||
.shell => try executeShellCmd(pwd.pw_shell.?, options),
|
||||
.xinitrc, .x11 => if (build_options.enable_x11_support) {
|
||||
var vt_buf: [5]u8 = undefined;
|
||||
const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{options.tty});
|
||||
try executeX11Cmd(pwd.pw_shell.?, pwd.pw_dir.?, options, current_environment.cmd, vt);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn initEnv(pwd: *interop.pwd.passwd, path_env: ?[:0]const u8) !void {
|
||||
_ = interop.stdlib.setenv("HOME", pwd.pw_dir, 1);
|
||||
_ = interop.stdlib.setenv("PWD", pwd.pw_dir, 1);
|
||||
_ = interop.stdlib.setenv("SHELL", pwd.pw_shell, 1);
|
||||
_ = interop.stdlib.setenv("USER", pwd.pw_name, 1);
|
||||
_ = interop.stdlib.setenv("LOGNAME", pwd.pw_name, 1);
|
||||
|
||||
if (path_env) |path| {
|
||||
const status = interop.stdlib.setenv("PATH", path, 1);
|
||||
if (status != 0) return error.SetPathFailed;
|
||||
}
|
||||
}
|
||||
|
||||
fn setXdgSessionEnv(display_server: enums.DisplayServer) void {
|
||||
_ = interop.stdlib.setenv("XDG_SESSION_TYPE", switch (display_server) {
|
||||
.wayland => "wayland",
|
||||
.shell => "tty",
|
||||
.xinitrc, .x11 => "x11",
|
||||
}, 0);
|
||||
}
|
||||
|
||||
fn setXdgEnv(tty_str: [:0]u8, maybe_desktop_name: ?[:0]const u8, maybe_xdg_desktop_names: ?[:0]const u8) !void {
|
||||
// The "/run/user/%d" directory is not available on FreeBSD. It is much
|
||||
// better to stick to the defaults and let applications using
|
||||
// XDG_RUNTIME_DIR to fall back to directories inside user's home
|
||||
// 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 (maybe_xdg_desktop_names) |xdg_desktop_names| _ = interop.stdlib.setenv("XDG_CURRENT_DESKTOP", xdg_desktop_names, 0);
|
||||
_ = interop.stdlib.setenv("XDG_SESSION_CLASS", "user", 0);
|
||||
_ = interop.stdlib.setenv("XDG_SESSION_ID", "1", 0);
|
||||
if (maybe_desktop_name) |desktop_name| _ = interop.stdlib.setenv("XDG_SESSION_DESKTOP", desktop_name, 0);
|
||||
_ = interop.stdlib.setenv("XDG_SEAT", "seat0", 0);
|
||||
_ = interop.stdlib.setenv("XDG_VTNR", tty_str, 0);
|
||||
}
|
||||
|
||||
fn loginConv(
|
||||
num_msg: c_int,
|
||||
msg: ?[*]?*const interop.pam.pam_message,
|
||||
resp: ?*?[*]interop.pam.pam_response,
|
||||
appdata_ptr: ?*anyopaque,
|
||||
) callconv(.C) c_int {
|
||||
const message_count: u32 = @intCast(num_msg);
|
||||
const messages = msg.?;
|
||||
|
||||
const allocator = std.heap.c_allocator;
|
||||
const response = allocator.alloc(interop.pam.pam_response, message_count) catch return interop.pam.PAM_BUF_ERR;
|
||||
|
||||
// Initialise allocated memory to 0
|
||||
// This ensures memory can be freed by pam on success
|
||||
@memset(response, std.mem.zeroes(interop.pam.pam_response));
|
||||
|
||||
var username: ?[:0]u8 = null;
|
||||
var password: ?[:0]u8 = null;
|
||||
var status: c_int = interop.pam.PAM_SUCCESS;
|
||||
|
||||
for (0..message_count) |i| set_credentials: {
|
||||
switch (messages[i].?.msg_style) {
|
||||
interop.pam.PAM_PROMPT_ECHO_ON => {
|
||||
const data: [*][*:0]u8 = @ptrCast(@alignCast(appdata_ptr));
|
||||
username = allocator.dupeZ(u8, std.mem.span(data[0])) catch {
|
||||
status = interop.pam.PAM_BUF_ERR;
|
||||
break :set_credentials;
|
||||
};
|
||||
response[i].resp = username.?;
|
||||
},
|
||||
interop.pam.PAM_PROMPT_ECHO_OFF => {
|
||||
const data: [*][*:0]u8 = @ptrCast(@alignCast(appdata_ptr));
|
||||
password = allocator.dupeZ(u8, std.mem.span(data[1])) catch {
|
||||
status = interop.pam.PAM_BUF_ERR;
|
||||
break :set_credentials;
|
||||
};
|
||||
response[i].resp = password.?;
|
||||
},
|
||||
interop.pam.PAM_ERROR_MSG => {
|
||||
status = interop.pam.PAM_CONV_ERR;
|
||||
break :set_credentials;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
if (status != interop.pam.PAM_SUCCESS) {
|
||||
// Memory is freed by pam otherwise
|
||||
allocator.free(response);
|
||||
if (username != null) allocator.free(username.?);
|
||||
if (password != null) allocator.free(password.?);
|
||||
} else {
|
||||
resp.?.* = response.ptr;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
fn getFreeDisplay() !u8 {
|
||||
var buf: [15]u8 = undefined;
|
||||
var i: u8 = 0;
|
||||
while (i < 200) : (i += 1) {
|
||||
const xlock = try std.fmt.bufPrint(&buf, "/tmp/.X{d}-lock", .{i});
|
||||
std.posix.access(xlock, std.posix.F_OK) catch break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
fn getXPid(display_num: u8) !i32 {
|
||||
var buf: [15]u8 = undefined;
|
||||
const file_name = try std.fmt.bufPrint(&buf, "/tmp/.X{d}-lock", .{display_num});
|
||||
const file = try std.fs.openFileAbsolute(file_name, .{});
|
||||
defer file.close();
|
||||
|
||||
var file_buf: [20]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&file_buf);
|
||||
|
||||
_ = try file.reader().streamUntilDelimiter(fbs.writer(), '\n', 20);
|
||||
const line = fbs.getWritten();
|
||||
|
||||
return std.fmt.parseInt(i32, std.mem.trim(u8, line, " "), 10);
|
||||
}
|
||||
|
||||
fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 {
|
||||
var xauth_buf: [100]u8 = undefined;
|
||||
var xauth_dir: [:0]const u8 = undefined;
|
||||
const xdg_rt_dir = std.posix.getenv("XDG_RUNTIME_DIR");
|
||||
var xauth_file: []const u8 = "lyxauth";
|
||||
|
||||
if (xdg_rt_dir == null) {
|
||||
const xdg_cfg_home = std.posix.getenv("XDG_CONFIG_HOME");
|
||||
var sb: std.c.Stat = undefined;
|
||||
if (xdg_cfg_home == null) {
|
||||
xauth_dir = try std.fmt.bufPrintZ(&xauth_buf, "{s}/.config", .{pwd});
|
||||
_ = std.c.stat(xauth_dir, &sb);
|
||||
const mode = sb.mode & std.posix.S.IFMT;
|
||||
if (mode == std.posix.S.IFDIR) {
|
||||
xauth_dir = try std.fmt.bufPrintZ(&xauth_buf, "{s}/ly", .{xauth_dir});
|
||||
} else {
|
||||
xauth_dir = pwd;
|
||||
xauth_file = ".lyxauth";
|
||||
}
|
||||
} else {
|
||||
xauth_dir = try std.fmt.bufPrintZ(&xauth_buf, "{s}/ly", .{xdg_cfg_home.?});
|
||||
}
|
||||
|
||||
_ = std.c.stat(xauth_dir, &sb);
|
||||
const mode = sb.mode & std.posix.S.IFMT;
|
||||
if (mode != std.posix.S.IFDIR) {
|
||||
std.posix.mkdir(xauth_dir, 777) catch {
|
||||
xauth_dir = pwd;
|
||||
xauth_file = ".lyxauth";
|
||||
};
|
||||
}
|
||||
} else {
|
||||
xauth_dir = xdg_rt_dir.?;
|
||||
}
|
||||
|
||||
// Trim trailing slashes
|
||||
var i = xauth_dir.len - 1;
|
||||
while (xauth_dir[i] == '/') i -= 1;
|
||||
const trimmed_xauth_dir = xauth_dir[0 .. i + 1];
|
||||
|
||||
var buf: [256]u8 = undefined;
|
||||
const xauthority: [:0]u8 = try std.fmt.bufPrintZ(&buf, "{s}/{s}", .{ trimmed_xauth_dir, xauth_file });
|
||||
const file = try std.fs.createFileAbsoluteZ(xauthority, .{});
|
||||
file.close();
|
||||
|
||||
return xauthority;
|
||||
}
|
||||
|
||||
fn mcookie() [Md5.digest_length * 2]u8 {
|
||||
var buf: [4096]u8 = undefined;
|
||||
std.crypto.random.bytes(&buf);
|
||||
|
||||
var out: [Md5.digest_length]u8 = undefined;
|
||||
Md5.hash(&buf, &out, .{});
|
||||
|
||||
return std.fmt.bytesToHex(&out, .lower);
|
||||
}
|
||||
|
||||
fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptions) !void {
|
||||
var pwd_buf: [100]u8 = undefined;
|
||||
const pwd = try std.fmt.bufPrintZ(&pwd_buf, "{s}", .{pw_dir});
|
||||
|
||||
const xauthority = try createXauthFile(pwd);
|
||||
_ = interop.stdlib.setenv("XAUTHORITY", xauthority, 1);
|
||||
_ = interop.stdlib.setenv("DISPLAY", display_name, 1);
|
||||
|
||||
const magic_cookie = mcookie();
|
||||
|
||||
const pid = try std.posix.fork();
|
||||
if (pid == 0) {
|
||||
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 args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
|
||||
std.posix.execveZ(shell, &args, std.c.environ) catch {};
|
||||
std.process.exit(1);
|
||||
}
|
||||
|
||||
const status = std.posix.waitpid(pid, 0);
|
||||
if (status.status != 0) return error.XauthFailed;
|
||||
}
|
||||
|
||||
fn executeShellCmd(shell: [*:0]const u8, options: AuthOptions) !void {
|
||||
// We don't want to redirect stdout and stderr in a shell session
|
||||
|
||||
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 args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
|
||||
return std.posix.execveZ(shell, &args, std.c.environ);
|
||||
}
|
||||
|
||||
fn executeWaylandCmd(shell: [*:0]const u8, options: AuthOptions, desktop_cmd: []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();
|
||||
var buf: [5]u8 = undefined;
|
||||
const display_name = try std.fmt.bufPrintZ(&buf, ":{d}", .{display_num});
|
||||
try xauth(display_name, shell, pw_dir, options);
|
||||
|
||||
const pid = try std.posix.fork();
|
||||
if (pid == 0) {
|
||||
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 args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
|
||||
std.posix.execveZ(shell, &args, std.c.environ) catch {};
|
||||
std.process.exit(1);
|
||||
}
|
||||
|
||||
var ok: c_int = undefined;
|
||||
var xcb: ?*interop.xcb.xcb_connection_t = null;
|
||||
while (ok != 0) {
|
||||
xcb = interop.xcb.xcb_connect(null, null);
|
||||
ok = interop.xcb.xcb_connection_has_error(xcb);
|
||||
std.posix.kill(pid, 0) catch |e| {
|
||||
if (e == error.ProcessNotFound and ok != 0) return error.XcbConnectionFailed;
|
||||
};
|
||||
}
|
||||
|
||||
// X Server detaches from the process.
|
||||
// PID can be fetched from /tmp/X{d}.lock
|
||||
const x_pid = try getXPid(display_num);
|
||||
|
||||
xorg_pid = try std.posix.fork();
|
||||
if (xorg_pid == 0) {
|
||||
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 args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
|
||||
std.posix.execveZ(shell, &args, std.c.environ) catch {};
|
||||
std.process.exit(1);
|
||||
}
|
||||
|
||||
// If we receive SIGTERM, clean up by killing the xorg_pid process
|
||||
const act = std.posix.Sigaction{
|
||||
.handler = .{ .handler = &xorgSignalHandler },
|
||||
.mask = std.posix.empty_sigset,
|
||||
.flags = 0,
|
||||
};
|
||||
std.posix.sigaction(std.posix.SIG.TERM, &act, null);
|
||||
|
||||
_ = std.posix.waitpid(xorg_pid, 0);
|
||||
interop.xcb.xcb_disconnect(xcb);
|
||||
|
||||
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;
|
||||
_ = 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 {
|
||||
entry.ut_type = utmp.USER_PROCESS;
|
||||
entry.ut_pid = pid;
|
||||
|
||||
var buf: [4096]u8 = undefined;
|
||||
const ttyname = try std.os.getFdPath(std.posix.STDIN_FILENO, &buf);
|
||||
|
||||
var ttyname_buf: [@sizeOf(@TypeOf(entry.ut_line))]u8 = undefined;
|
||||
_ = try std.fmt.bufPrintZ(&ttyname_buf, "{s}", .{ttyname["/dev/".len..]});
|
||||
|
||||
entry.ut_line = ttyname_buf;
|
||||
entry.ut_id = ttyname_buf["tty".len..7].*;
|
||||
|
||||
var username_buf: [@sizeOf(@TypeOf(entry.ut_user))]u8 = undefined;
|
||||
_ = try std.fmt.bufPrintZ(&username_buf, "{s}", .{username});
|
||||
|
||||
entry.ut_user = username_buf;
|
||||
|
||||
var host: [@sizeOf(@TypeOf(entry.ut_host))]u8 = undefined;
|
||||
host[0] = 0;
|
||||
entry.ut_host = host;
|
||||
|
||||
var tv: interop.system_time.timeval = undefined;
|
||||
_ = interop.system_time.gettimeofday(&tv, null);
|
||||
|
||||
entry.ut_tv = .{
|
||||
.tv_sec = @intCast(tv.tv_sec),
|
||||
.tv_usec = @intCast(tv.tv_usec),
|
||||
};
|
||||
entry.ut_addr_v6[0] = 0;
|
||||
|
||||
utmp.setutxent();
|
||||
_ = utmp.pututxline(entry);
|
||||
utmp.endutxent();
|
||||
}
|
||||
|
||||
fn removeUtmpEntry(entry: *Utmp) void {
|
||||
entry.ut_type = utmp.DEAD_PROCESS;
|
||||
entry.ut_line[0] = 0;
|
||||
entry.ut_user[0] = 0;
|
||||
utmp.setutxent();
|
||||
_ = utmp.pututxline(entry);
|
||||
utmp.endutxent();
|
||||
}
|
||||
|
||||
fn pamDiagnose(status: c_int) anyerror {
|
||||
return switch (status) {
|
||||
interop.pam.PAM_ACCT_EXPIRED => return error.PamAccountExpired,
|
||||
interop.pam.PAM_AUTH_ERR => return error.PamAuthError,
|
||||
interop.pam.PAM_AUTHINFO_UNAVAIL => return error.PamAuthInfoUnavailable,
|
||||
interop.pam.PAM_BUF_ERR => return error.PamBufferError,
|
||||
interop.pam.PAM_CRED_ERR => return error.PamCredentialsError,
|
||||
interop.pam.PAM_CRED_EXPIRED => return error.PamCredentialsExpired,
|
||||
interop.pam.PAM_CRED_INSUFFICIENT => return error.PamCredentialsInsufficient,
|
||||
interop.pam.PAM_CRED_UNAVAIL => return error.PamCredentialsUnavailable,
|
||||
interop.pam.PAM_MAXTRIES => return error.PamMaximumTries,
|
||||
interop.pam.PAM_NEW_AUTHTOK_REQD => return error.PamNewAuthTokenRequired,
|
||||
interop.pam.PAM_PERM_DENIED => return error.PamPermissionDenied,
|
||||
interop.pam.PAM_SESSION_ERR => return error.PamSessionError,
|
||||
interop.pam.PAM_SYSTEM_ERR => return error.PamSystemError,
|
||||
interop.pam.PAM_USER_UNKNOWN => return error.PamUserUnknown,
|
||||
else => return error.PamAbort,
|
||||
};
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
const std = @import("std");
|
||||
const interop = @import("interop.zig");
|
||||
const enums = @import("enums.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;
|
||||
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 {
|
||||
var cells: [SIZE]Cell = undefined;
|
||||
|
||||
var tv: interop.system_time.timeval = undefined;
|
||||
_ = interop.system_time.gettimeofday(&tv, null);
|
||||
|
||||
const clock_chars = toBigNumber(if (animate and char == ':' and @divTrunc(tv.tv_usec, 500000) != 0) ' ' else char, bigclock);
|
||||
for (0..cells.len) |i| cells[i] = Cell.init(clock_chars[i], fg, bg);
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
pub fn alphaBlit(x: usize, y: usize, tb_width: usize, tb_height: usize, cells: [SIZE]Cell) void {
|
||||
if (x + WIDTH >= tb_width or y + HEIGHT >= tb_height) return;
|
||||
|
||||
for (0..HEIGHT) |yy| {
|
||||
for (0..WIDTH) |xx| {
|
||||
const cell = cells[yy * WIDTH + xx];
|
||||
cell.put(x + xx, y + yy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn toBigNumber(char: u8, bigclock: Bigclock) [SIZE]u21 {
|
||||
const locale_chars = switch (bigclock) {
|
||||
.fa => fa.locale_chars,
|
||||
.en => en.locale_chars,
|
||||
.none => unreachable,
|
||||
};
|
||||
return switch (char) {
|
||||
'0' => locale_chars.ZERO,
|
||||
'1' => locale_chars.ONE,
|
||||
'2' => locale_chars.TWO,
|
||||
'3' => locale_chars.THREE,
|
||||
'4' => locale_chars.FOUR,
|
||||
'5' => locale_chars.FIVE,
|
||||
'6' => locale_chars.SIX,
|
||||
'7' => locale_chars.SEVEN,
|
||||
'8' => locale_chars.EIGHT,
|
||||
'9' => locale_chars.NINE,
|
||||
':' => locale_chars.S,
|
||||
else => locale_chars.E,
|
||||
};
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
367
src/config.c
Normal file
367
src/config.c
Normal file
@@ -0,0 +1,367 @@
|
||||
#include "configator.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef DEBUG
|
||||
#define INI_LANG DATADIR "/lang/%s.ini"
|
||||
#define INI_CONFIG "/etc/ly/config.ini"
|
||||
#else
|
||||
#define INI_LANG "../res/lang/%s.ini"
|
||||
#define INI_CONFIG "../res/config.ini"
|
||||
#endif
|
||||
|
||||
static void lang_handle(void* data, char** pars, const int pars_count)
|
||||
{
|
||||
if (*((char**)data) != NULL)
|
||||
{
|
||||
free (*((char**)data));
|
||||
}
|
||||
|
||||
*((char**)data) = strdup(*pars);
|
||||
}
|
||||
|
||||
static void config_handle_u8(void* data, char** pars, const int pars_count)
|
||||
{
|
||||
if (strcmp(*pars, "") == 0)
|
||||
{
|
||||
*((u8*)data) = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
*((u8*)data) = atoi(*pars);
|
||||
}
|
||||
}
|
||||
|
||||
static void config_handle_u16(void* data, char** pars, const int pars_count)
|
||||
{
|
||||
if (strcmp(*pars, "") == 0)
|
||||
{
|
||||
*((u16*)data) = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
*((u16*)data) = atoi(*pars);
|
||||
}
|
||||
}
|
||||
|
||||
void config_handle_str(void* data, char** pars, const int pars_count)
|
||||
{
|
||||
if (*((char**)data) != NULL)
|
||||
{
|
||||
free(*((char**)data));
|
||||
}
|
||||
|
||||
*((char**)data) = strdup(*pars);
|
||||
}
|
||||
|
||||
static void config_handle_char(void* data, char** pars, const int pars_count)
|
||||
{
|
||||
*((char*)data) = **pars;
|
||||
}
|
||||
|
||||
static void config_handle_bool(void* data, char** pars, const int pars_count)
|
||||
{
|
||||
*((bool*)data) = (strcmp("true", *pars) == 0);
|
||||
}
|
||||
|
||||
void lang_load()
|
||||
{
|
||||
// must be alphabetically sorted
|
||||
struct configator_param map_no_section[] =
|
||||
{
|
||||
{"capslock", &lang.capslock, lang_handle},
|
||||
{"err_alloc", &lang.err_alloc, lang_handle},
|
||||
{"err_bounds", &lang.err_bounds, lang_handle},
|
||||
{"err_chdir", &lang.err_chdir, lang_handle},
|
||||
{"err_console_dev", &lang.err_console_dev, lang_handle},
|
||||
{"err_dgn_oob", &lang.err_dgn_oob, lang_handle},
|
||||
{"err_domain", &lang.err_domain, lang_handle},
|
||||
{"err_hostname", &lang.err_hostname, lang_handle},
|
||||
{"err_mlock", &lang.err_mlock, lang_handle},
|
||||
{"err_null", &lang.err_null, lang_handle},
|
||||
{"err_pam", &lang.err_pam, lang_handle},
|
||||
{"err_pam_abort", &lang.err_pam_abort, lang_handle},
|
||||
{"err_pam_acct_expired", &lang.err_pam_acct_expired, lang_handle},
|
||||
{"err_pam_auth", &lang.err_pam_auth, lang_handle},
|
||||
{"err_pam_authinfo_unavail", &lang.err_pam_authinfo_unavail, lang_handle},
|
||||
{"err_pam_authok_reqd", &lang.err_pam_authok_reqd, lang_handle},
|
||||
{"err_pam_buf", &lang.err_pam_buf, lang_handle},
|
||||
{"err_pam_cred_err", &lang.err_pam_cred_err, lang_handle},
|
||||
{"err_pam_cred_expired", &lang.err_pam_cred_expired, lang_handle},
|
||||
{"err_pam_cred_insufficient", &lang.err_pam_cred_insufficient, lang_handle},
|
||||
{"err_pam_cred_unavail", &lang.err_pam_cred_unavail, lang_handle},
|
||||
{"err_pam_maxtries", &lang.err_pam_maxtries, lang_handle},
|
||||
{"err_pam_perm_denied", &lang.err_pam_perm_denied, lang_handle},
|
||||
{"err_pam_session", &lang.err_pam_session, lang_handle},
|
||||
{"err_pam_sys", &lang.err_pam_sys, lang_handle},
|
||||
{"err_pam_user_unknown", &lang.err_pam_user_unknown, lang_handle},
|
||||
{"err_path", &lang.err_path, lang_handle},
|
||||
{"err_perm_dir", &lang.err_perm_dir, lang_handle},
|
||||
{"err_perm_group", &lang.err_perm_group, lang_handle},
|
||||
{"err_perm_user", &lang.err_perm_user, lang_handle},
|
||||
{"err_pwnam", &lang.err_pwnam, lang_handle},
|
||||
{"err_user_gid", &lang.err_user_gid, lang_handle},
|
||||
{"err_user_init", &lang.err_user_init, lang_handle},
|
||||
{"err_user_uid", &lang.err_user_uid, lang_handle},
|
||||
{"err_xsessions_dir", &lang.err_xsessions_dir, lang_handle},
|
||||
{"err_xsessions_open", &lang.err_xsessions_open, lang_handle},
|
||||
{"f1", &lang.f1, lang_handle},
|
||||
{"f2", &lang.f2, lang_handle},
|
||||
{"login", &lang.login, lang_handle},
|
||||
{"logout", &lang.logout, lang_handle},
|
||||
{"numlock", &lang.numlock, lang_handle},
|
||||
{"password", &lang.password, lang_handle},
|
||||
{"shell", &lang.shell, lang_handle},
|
||||
{"wayland", &lang.wayland, lang_handle},
|
||||
{"xinitrc", &lang.xinitrc, lang_handle},
|
||||
};
|
||||
|
||||
uint16_t map_len[] = {45};
|
||||
struct configator_param* map[] =
|
||||
{
|
||||
map_no_section,
|
||||
};
|
||||
|
||||
uint16_t sections_len = 0;
|
||||
struct configator_param* sections = NULL;
|
||||
|
||||
struct configator lang;
|
||||
lang.map = map;
|
||||
lang.map_len = map_len;
|
||||
lang.sections = sections;
|
||||
lang.sections_len = sections_len;
|
||||
|
||||
char file[256];
|
||||
snprintf(file, 256, INI_LANG, config.lang);
|
||||
|
||||
if (access(file, F_OK) != -1)
|
||||
{
|
||||
configator(&lang, file);
|
||||
}
|
||||
}
|
||||
|
||||
void config_load(const char *cfg_path)
|
||||
{
|
||||
if (cfg_path == NULL)
|
||||
{
|
||||
cfg_path = INI_CONFIG;
|
||||
}
|
||||
// must be alphabetically sorted
|
||||
struct configator_param map_no_section[] =
|
||||
{
|
||||
{"animate", &config.animate, config_handle_bool},
|
||||
{"animation", &config.animation, config_handle_u8},
|
||||
{"asterisk", &config.asterisk, config_handle_char},
|
||||
{"bg", &config.bg, config_handle_u8},
|
||||
{"blank_box", &config.blank_box, config_handle_bool},
|
||||
{"blank_password", &config.blank_password, config_handle_bool},
|
||||
{"console_dev", &config.console_dev, config_handle_str},
|
||||
{"default_input", &config.default_input, config_handle_u8},
|
||||
{"fg", &config.fg, config_handle_u8},
|
||||
{"hide_borders", &config.hide_borders, config_handle_bool},
|
||||
{"input_len", &config.input_len, config_handle_u8},
|
||||
{"lang", &config.lang, config_handle_str},
|
||||
{"load", &config.load, config_handle_bool},
|
||||
{"margin_box_h", &config.margin_box_h, config_handle_u8},
|
||||
{"margin_box_v", &config.margin_box_v, config_handle_u8},
|
||||
{"max_desktop_len", &config.max_desktop_len, config_handle_u8},
|
||||
{"max_login_len", &config.max_login_len, config_handle_u8},
|
||||
{"max_password_len", &config.max_password_len, config_handle_u8},
|
||||
{"mcookie_cmd", &config.mcookie_cmd, config_handle_str},
|
||||
{"min_refresh_delta", &config.min_refresh_delta, config_handle_u16},
|
||||
{"path", &config.path, config_handle_str},
|
||||
{"restart_cmd", &config.restart_cmd, config_handle_str},
|
||||
{"save", &config.save, config_handle_bool},
|
||||
{"save_file", &config.save_file, config_handle_str},
|
||||
{"service_name", &config.service_name, config_handle_str},
|
||||
{"shutdown_cmd", &config.shutdown_cmd, config_handle_str},
|
||||
{"term_reset_cmd", &config.term_reset_cmd, config_handle_str},
|
||||
{"tty", &config.tty, config_handle_u8},
|
||||
{"wayland_cmd", &config.wayland_cmd, config_handle_str},
|
||||
{"wayland_specifier", &config.wayland_specifier, config_handle_bool},
|
||||
{"waylandsessions", &config.waylandsessions, config_handle_str},
|
||||
{"x_cmd", &config.x_cmd, config_handle_str},
|
||||
{"x_cmd_setup", &config.x_cmd_setup, config_handle_str},
|
||||
{"xauth_cmd", &config.xauth_cmd, config_handle_str},
|
||||
{"xsessions", &config.xsessions, config_handle_str},
|
||||
};
|
||||
|
||||
uint16_t map_len[] = {34};
|
||||
struct configator_param* map[] =
|
||||
{
|
||||
map_no_section,
|
||||
};
|
||||
|
||||
uint16_t sections_len = 0;
|
||||
struct configator_param* sections = NULL;
|
||||
|
||||
struct configator config;
|
||||
config.map = map;
|
||||
config.map_len = map_len;
|
||||
config.sections = sections;
|
||||
config.sections_len = sections_len;
|
||||
|
||||
configator(&config, (char *) cfg_path);
|
||||
}
|
||||
|
||||
void lang_defaults()
|
||||
{
|
||||
lang.capslock = strdup("capslock");
|
||||
lang.err_alloc = strdup("failed memory allocation");
|
||||
lang.err_bounds = strdup("out-of-bounds index");
|
||||
lang.err_chdir = strdup("failed to open home folder");
|
||||
lang.err_console_dev = strdup("failed to access console");
|
||||
lang.err_dgn_oob = strdup("log message");
|
||||
lang.err_domain = strdup("invalid domain");
|
||||
lang.err_hostname = strdup("failed to get hostname");
|
||||
lang.err_mlock = strdup("failed to lock password memory");
|
||||
lang.err_null = strdup("null pointer");
|
||||
lang.err_pam = strdup("pam transaction failed");
|
||||
lang.err_pam_abort = strdup("pam transaction aborted");
|
||||
lang.err_pam_acct_expired = strdup("account expired");
|
||||
lang.err_pam_auth = strdup("authentication error");
|
||||
lang.err_pam_authinfo_unavail = strdup("failed to get user info");
|
||||
lang.err_pam_authok_reqd = strdup("token expired");
|
||||
lang.err_pam_buf = strdup("memory buffer error");
|
||||
lang.err_pam_cred_err = strdup("failed to set credentials");
|
||||
lang.err_pam_cred_expired = strdup("credentials expired");
|
||||
lang.err_pam_cred_insufficient = strdup("insufficient credentials");
|
||||
lang.err_pam_cred_unavail = strdup("failed to get credentials");
|
||||
lang.err_pam_maxtries = strdup("reached maximum tries limit");
|
||||
lang.err_pam_perm_denied = strdup("permission denied");
|
||||
lang.err_pam_session = strdup("session error");
|
||||
lang.err_pam_sys = strdup("system error");
|
||||
lang.err_pam_user_unknown = strdup("unknown user");
|
||||
lang.err_path = strdup("failed to set path");
|
||||
lang.err_perm_dir = strdup("failed to change current directory");
|
||||
lang.err_perm_group = strdup("failed to downgrade group permissions");
|
||||
lang.err_perm_user = strdup("failed to downgrade user permissions");
|
||||
lang.err_pwnam = strdup("failed to get user info");
|
||||
lang.err_user_gid = strdup("failed to set user GID");
|
||||
lang.err_user_init = strdup("failed to initialize user");
|
||||
lang.err_user_uid = strdup("failed to set user UID");
|
||||
lang.err_xsessions_dir = strdup("failed to find sessions folder");
|
||||
lang.err_xsessions_open = strdup("failed to open sessions folder");
|
||||
lang.f1 = strdup("F1 shutdown");
|
||||
lang.f2 = strdup("F2 reboot");
|
||||
lang.login = strdup("login:");
|
||||
lang.logout = strdup("logged out");
|
||||
lang.numlock = strdup("numlock");
|
||||
lang.password = strdup("password:");
|
||||
lang.shell = strdup("shell");
|
||||
lang.wayland = strdup("wayland");
|
||||
lang.xinitrc = strdup("xinitrc");
|
||||
}
|
||||
|
||||
void config_defaults()
|
||||
{
|
||||
config.animate = false;
|
||||
config.animation = 0;
|
||||
config.asterisk = '*';
|
||||
config.bg = 0;
|
||||
config.blank_box = true;
|
||||
config.blank_password = false;
|
||||
config.console_dev = strdup("/dev/console");
|
||||
config.default_input = PASSWORD_INPUT;
|
||||
config.fg = 9;
|
||||
config.hide_borders = false;
|
||||
config.input_len = 34;
|
||||
config.lang = strdup("en");
|
||||
config.load = true;
|
||||
config.margin_box_h = 2;
|
||||
config.margin_box_v = 1;
|
||||
config.max_desktop_len = 100;
|
||||
config.max_login_len = 255;
|
||||
config.max_password_len = 255;
|
||||
config.mcookie_cmd = strdup("/usr/bin/mcookie");
|
||||
config.min_refresh_delta = 5;
|
||||
config.path = strdup("/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin");
|
||||
config.restart_cmd = strdup("/sbin/shutdown -r now");
|
||||
config.save = true;
|
||||
config.save_file = strdup("/etc/ly/save");
|
||||
config.service_name = strdup("ly");
|
||||
config.shutdown_cmd = strdup("/sbin/shutdown -a now");
|
||||
config.term_reset_cmd = strdup("/usr/bin/tput reset");
|
||||
config.tty = 2;
|
||||
config.wayland_cmd = strdup(DATADIR "/wsetup.sh");
|
||||
config.wayland_specifier = false;
|
||||
config.waylandsessions = strdup("/usr/share/wayland-sessions");
|
||||
config.x_cmd = strdup("/usr/bin/X");
|
||||
config.x_cmd_setup = strdup(DATADIR "/xsetup.sh");
|
||||
config.xauth_cmd = strdup("/usr/bin/xauth");
|
||||
config.xsessions = strdup("/usr/share/xsessions");
|
||||
}
|
||||
|
||||
void lang_free()
|
||||
{
|
||||
free(lang.capslock);
|
||||
free(lang.err_alloc);
|
||||
free(lang.err_bounds);
|
||||
free(lang.err_chdir);
|
||||
free(lang.err_console_dev);
|
||||
free(lang.err_dgn_oob);
|
||||
free(lang.err_domain);
|
||||
free(lang.err_hostname);
|
||||
free(lang.err_mlock);
|
||||
free(lang.err_null);
|
||||
free(lang.err_pam);
|
||||
free(lang.err_pam_abort);
|
||||
free(lang.err_pam_acct_expired);
|
||||
free(lang.err_pam_auth);
|
||||
free(lang.err_pam_authinfo_unavail);
|
||||
free(lang.err_pam_authok_reqd);
|
||||
free(lang.err_pam_buf);
|
||||
free(lang.err_pam_cred_err);
|
||||
free(lang.err_pam_cred_expired);
|
||||
free(lang.err_pam_cred_insufficient);
|
||||
free(lang.err_pam_cred_unavail);
|
||||
free(lang.err_pam_maxtries);
|
||||
free(lang.err_pam_perm_denied);
|
||||
free(lang.err_pam_session);
|
||||
free(lang.err_pam_sys);
|
||||
free(lang.err_pam_user_unknown);
|
||||
free(lang.err_path);
|
||||
free(lang.err_perm_dir);
|
||||
free(lang.err_perm_group);
|
||||
free(lang.err_perm_user);
|
||||
free(lang.err_pwnam);
|
||||
free(lang.err_user_gid);
|
||||
free(lang.err_user_init);
|
||||
free(lang.err_user_uid);
|
||||
free(lang.err_xsessions_dir);
|
||||
free(lang.err_xsessions_open);
|
||||
free(lang.f1);
|
||||
free(lang.f2);
|
||||
free(lang.login);
|
||||
free(lang.logout);
|
||||
free(lang.numlock);
|
||||
free(lang.password);
|
||||
free(lang.shell);
|
||||
free(lang.wayland);
|
||||
free(lang.xinitrc);
|
||||
}
|
||||
|
||||
void config_free()
|
||||
{
|
||||
free(config.console_dev);
|
||||
free(config.lang);
|
||||
free(config.mcookie_cmd);
|
||||
free(config.path);
|
||||
free(config.restart_cmd);
|
||||
free(config.save_file);
|
||||
free(config.service_name);
|
||||
free(config.shutdown_cmd);
|
||||
free(config.term_reset_cmd);
|
||||
free(config.wayland_cmd);
|
||||
free(config.waylandsessions);
|
||||
free(config.x_cmd);
|
||||
free(config.x_cmd_setup);
|
||||
free(config.xauth_cmd);
|
||||
free(config.xsessions);
|
||||
}
|
||||
111
src/config.h
Normal file
111
src/config.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#ifndef H_LY_CONFIG
|
||||
#define H_LY_CONFIG
|
||||
|
||||
#include "ctypes.h"
|
||||
|
||||
enum INPUTS {
|
||||
SESSION_SWITCH,
|
||||
LOGIN_INPUT,
|
||||
PASSWORD_INPUT,
|
||||
};
|
||||
|
||||
struct lang
|
||||
{
|
||||
char* capslock;
|
||||
char* err_alloc;
|
||||
char* err_bounds;
|
||||
char* err_chdir;
|
||||
char* err_console_dev;
|
||||
char* err_dgn_oob;
|
||||
char* err_domain;
|
||||
char* err_hostname;
|
||||
char* err_mlock;
|
||||
char* err_null;
|
||||
char* err_pam;
|
||||
char* err_pam_abort;
|
||||
char* err_pam_acct_expired;
|
||||
char* err_pam_auth;
|
||||
char* err_pam_authinfo_unavail;
|
||||
char* err_pam_authok_reqd;
|
||||
char* err_pam_buf;
|
||||
char* err_pam_cred_err;
|
||||
char* err_pam_cred_expired;
|
||||
char* err_pam_cred_insufficient;
|
||||
char* err_pam_cred_unavail;
|
||||
char* err_pam_maxtries;
|
||||
char* err_pam_perm_denied;
|
||||
char* err_pam_session;
|
||||
char* err_pam_sys;
|
||||
char* err_pam_user_unknown;
|
||||
char* err_path;
|
||||
char* err_perm_dir;
|
||||
char* err_perm_group;
|
||||
char* err_perm_user;
|
||||
char* err_pwnam;
|
||||
char* err_user_gid;
|
||||
char* err_user_init;
|
||||
char* err_user_uid;
|
||||
char* err_xsessions_dir;
|
||||
char* err_xsessions_open;
|
||||
char* f1;
|
||||
char* f2;
|
||||
char* login;
|
||||
char* logout;
|
||||
char* numlock;
|
||||
char* password;
|
||||
char* shell;
|
||||
char* wayland;
|
||||
char* xinitrc;
|
||||
};
|
||||
|
||||
struct config
|
||||
{
|
||||
bool animate;
|
||||
u8 animation;
|
||||
char asterisk;
|
||||
u8 bg;
|
||||
bool blank_box;
|
||||
bool blank_password;
|
||||
char* console_dev;
|
||||
u8 default_input;
|
||||
u8 fg;
|
||||
bool hide_borders;
|
||||
u8 input_len;
|
||||
char* lang;
|
||||
bool load;
|
||||
u8 margin_box_h;
|
||||
u8 margin_box_v;
|
||||
u8 max_desktop_len;
|
||||
u8 max_login_len;
|
||||
u8 max_password_len;
|
||||
char* mcookie_cmd;
|
||||
u16 min_refresh_delta;
|
||||
char* path;
|
||||
char* restart_cmd;
|
||||
bool save;
|
||||
char* save_file;
|
||||
char* service_name;
|
||||
char* shutdown_cmd;
|
||||
char* term_reset_cmd;
|
||||
u8 tty;
|
||||
char* wayland_cmd;
|
||||
bool wayland_specifier;
|
||||
char* waylandsessions;
|
||||
char* x_cmd;
|
||||
char* x_cmd_setup;
|
||||
char* xauth_cmd;
|
||||
char* xsessions;
|
||||
};
|
||||
|
||||
extern struct lang lang;
|
||||
extern struct config config;
|
||||
|
||||
void config_handle_str(void* data, char** pars, const int pars_count);
|
||||
void lang_load();
|
||||
void config_load(const char *cfg_path);
|
||||
void lang_defaults();
|
||||
void config_defaults();
|
||||
void lang_free();
|
||||
void config_free();
|
||||
|
||||
#endif
|
||||
@@ -1,70 +0,0 @@
|
||||
const build_options = @import("build_options");
|
||||
const enums = @import("../enums.zig");
|
||||
|
||||
const Animation = enums.Animation;
|
||||
const Input = enums.Input;
|
||||
const ViMode = enums.ViMode;
|
||||
const Bigclock = enums.Bigclock;
|
||||
|
||||
allow_empty_password: bool = true,
|
||||
animation: Animation = .none,
|
||||
animation_timeout_sec: u12 = 0,
|
||||
asterisk: ?u32 = '*',
|
||||
auth_fails: u64 = 10,
|
||||
bg: u32 = 0x00000000,
|
||||
bigclock: Bigclock = .none,
|
||||
blank_box: bool = true,
|
||||
border_fg: u32 = 0x00FFFFFF,
|
||||
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,
|
||||
clock: ?[:0]const u8 = null,
|
||||
cmatrix_fg: u32 = 0x0000FF00,
|
||||
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,
|
||||
doom_top_color: u32 = 0x00FF0000,
|
||||
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_key_hints: bool = false,
|
||||
initial_info_text: ?[]const u8 = null,
|
||||
input_len: u8 = 34,
|
||||
lang: []const u8 = "en",
|
||||
load: bool = true,
|
||||
login_cmd: ?[]const u8 = null,
|
||||
logout_cmd: ?[]const u8 = null,
|
||||
margin_box_h: u8 = 2,
|
||||
margin_box_v: u8 = 1,
|
||||
min_refresh_delta: u16 = 5,
|
||||
numlock: bool = false,
|
||||
path: ?[:0]const u8 = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
restart_cmd: []const u8 = "/sbin/shutdown -r now",
|
||||
restart_key: []const u8 = "F2",
|
||||
save: bool = true,
|
||||
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_key: []const u8 = "F1",
|
||||
sleep_cmd: ?[]const u8 = null,
|
||||
sleep_key: []const u8 = "F3",
|
||||
text_in_center: bool = false,
|
||||
tty: u8 = build_options.tty,
|
||||
vi_default_mode: ViMode = .normal,
|
||||
vi_mode: bool = false,
|
||||
waylandsessions: []const u8 = build_options.prefix_directory ++ "/share/wayland-sessions",
|
||||
x_cmd: []const u8 = build_options.prefix_directory ++ "/bin/X",
|
||||
xauth_cmd: []const u8 = build_options.prefix_directory ++ "/bin/xauth",
|
||||
xinitrc: ?[]const u8 = "~/.xinitrc",
|
||||
xsessions: []const u8 = build_options.prefix_directory ++ "/share/xsessions",
|
||||
@@ -1,69 +0,0 @@
|
||||
//
|
||||
// NOTE: After editing this file, please run `/res/lang/normalize_lang_files.py`
|
||||
// to update all the language files accordingly.
|
||||
//
|
||||
|
||||
authenticating: []const u8 = "authenticating...",
|
||||
brightness_down: []const u8 = "decrease brightness",
|
||||
brightness_up: []const u8 = "increase brightness",
|
||||
capslock: []const u8 = "capslock",
|
||||
err_alloc: []const u8 = "failed memory allocation",
|
||||
err_bounds: []const u8 = "out-of-bounds index",
|
||||
err_brightness_change: []const u8 = "failed to change brightness",
|
||||
err_chdir: []const u8 = "failed to open home folder",
|
||||
err_config: []const u8 = "unable to parse config file",
|
||||
err_console_dev: []const u8 = "failed to access console",
|
||||
err_dgn_oob: []const u8 = "log message",
|
||||
err_domain: []const u8 = "invalid domain",
|
||||
err_empty_password: []const u8 = "empty password not allowed",
|
||||
err_envlist: []const u8 = "failed to get envlist",
|
||||
err_hostname: []const u8 = "failed to get hostname",
|
||||
err_mlock: []const u8 = "failed to lock password memory",
|
||||
err_null: []const u8 = "null pointer",
|
||||
err_numlock: []const u8 = "failed to set numlock",
|
||||
err_pam: []const u8 = "pam transaction failed",
|
||||
err_pam_abort: []const u8 = "pam transaction aborted",
|
||||
err_pam_acct_expired: []const u8 = "account expired",
|
||||
err_pam_auth: []const u8 = "authentication error",
|
||||
err_pam_authinfo_unavail: []const u8 = "failed to get user info",
|
||||
err_pam_authok_reqd: []const u8 = "token expired",
|
||||
err_pam_buf: []const u8 = "memory buffer error",
|
||||
err_pam_cred_err: []const u8 = "failed to set credentials",
|
||||
err_pam_cred_expired: []const u8 = "credentials expired",
|
||||
err_pam_cred_insufficient: []const u8 = "insufficient credentials",
|
||||
err_pam_cred_unavail: []const u8 = "failed to get credentials",
|
||||
err_pam_maxtries: []const u8 = "reached maximum tries limit",
|
||||
err_pam_perm_denied: []const u8 = "permission denied",
|
||||
err_pam_session: []const u8 = "session error",
|
||||
err_pam_sys: []const u8 = "system error",
|
||||
err_pam_user_unknown: []const u8 = "unknown user",
|
||||
err_path: []const u8 = "failed to set path",
|
||||
err_perm_dir: []const u8 = "failed to change current directory",
|
||||
err_perm_group: []const u8 = "failed to downgrade group permissions",
|
||||
err_perm_user: []const u8 = "failed to downgrade user permissions",
|
||||
err_pwnam: []const u8 = "failed to get user info",
|
||||
err_sleep: []const u8 = "failed to execute sleep command",
|
||||
err_tty_ctrl: []const u8 = "tty control transfer failed",
|
||||
err_unknown: []const u8 = "an unknown error occurred",
|
||||
err_user_gid: []const u8 = "failed to set user GID",
|
||||
err_user_init: []const u8 = "failed to initialize user",
|
||||
err_user_uid: []const u8 = "failed to set user UID",
|
||||
err_xauth: []const u8 = "xauth command failed",
|
||||
err_xcb_conn: []const u8 = "xcb connection failed",
|
||||
err_xsessions_dir: []const u8 = "failed to find sessions folder",
|
||||
err_xsessions_open: []const u8 = "failed to open sessions folder",
|
||||
insert: []const u8 = "insert",
|
||||
login: []const u8 = "login:",
|
||||
logout: []const u8 = "logged out",
|
||||
no_x11_support: []const u8 = "x11 support disabled at compile-time",
|
||||
normal: []const u8 = "normal",
|
||||
numlock: []const u8 = "numlock",
|
||||
other: []const u8 = "other",
|
||||
password: []const u8 = "password:",
|
||||
restart: []const u8 = "reboot",
|
||||
shell: [:0]const u8 = "shell",
|
||||
shutdown: []const u8 = "shutdown",
|
||||
sleep: []const u8 = "sleep",
|
||||
wayland: []const u8 = "wayland",
|
||||
x11: []const u8 = "x11",
|
||||
xinitrc: [:0]const u8 = "xinitrc",
|
||||
@@ -1,2 +0,0 @@
|
||||
user: ?[]const u8 = null,
|
||||
session_index: ?usize = null,
|
||||
@@ -1,203 +0,0 @@
|
||||
// The migrator ensures compatibility with <=0.6.0 configuration files
|
||||
|
||||
const std = @import("std");
|
||||
const ini = @import("zigini");
|
||||
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 {
|
||||
if (std.mem.eql(u8, field.key, "animate")) {
|
||||
// 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")) {
|
||||
// The option has simply been renamed
|
||||
var mapped_field = field;
|
||||
mapped_field.key = "clear_password";
|
||||
|
||||
mapped_config_fields = true;
|
||||
return mapped_field;
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, field.key, "default_input")) {
|
||||
// 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.
|
||||
// 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{};
|
||||
|
||||
if (maybe_save_file) |path| {
|
||||
defer temporary_allocator.free(path);
|
||||
|
||||
var file = std.fs.openFileAbsolute(path, .{}) catch return save;
|
||||
defer file.close();
|
||||
|
||||
const reader = file.reader();
|
||||
|
||||
var user_fbs = std.io.fixedBufferStream(user_buf);
|
||||
reader.streamUntilDelimiter(user_fbs.writer(), '\n', user_buf.len) catch return save;
|
||||
const user = user_fbs.getWritten();
|
||||
if (user.len > 0) save.user = user;
|
||||
|
||||
var session_buf: [20]u8 = undefined;
|
||||
var session_fbs = std.io.fixedBufferStream(&session_buf);
|
||||
reader.streamUntilDelimiter(session_fbs.writer(), '\n', session_buf.len) 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;
|
||||
}
|
||||
|
||||
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});
|
||||
}
|
||||
27
src/dragonfail_error.h
Normal file
27
src/dragonfail_error.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef H_DRAGONFAIL_ERROR
|
||||
#define H_DRAGONFAIL_ERROR
|
||||
|
||||
enum dgn_error
|
||||
{
|
||||
DGN_OK, // do not remove
|
||||
|
||||
DGN_NULL,
|
||||
DGN_ALLOC,
|
||||
DGN_BOUNDS,
|
||||
DGN_DOMAIN,
|
||||
DGN_MLOCK,
|
||||
DGN_XSESSIONS_DIR,
|
||||
DGN_XSESSIONS_OPEN,
|
||||
DGN_PATH,
|
||||
DGN_CHDIR,
|
||||
DGN_PWNAM,
|
||||
DGN_USER_INIT,
|
||||
DGN_USER_GID,
|
||||
DGN_USER_UID,
|
||||
DGN_PAM,
|
||||
DGN_HOSTNAME,
|
||||
|
||||
DGN_SIZE, // do not remove
|
||||
};
|
||||
|
||||
#endif
|
||||
647
src/draw.c
Normal file
647
src/draw.c
Normal file
@@ -0,0 +1,647 @@
|
||||
#include "dragonfail.h"
|
||||
#include "termbox.h"
|
||||
#include "ctypes.h"
|
||||
|
||||
#include "inputs.h"
|
||||
#include "utils.h"
|
||||
#include "config.h"
|
||||
#include "draw.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
#include <sys/kbio.h>
|
||||
#else // linux
|
||||
#include <linux/kd.h>
|
||||
#endif
|
||||
|
||||
#define DOOM_STEPS 13
|
||||
|
||||
void draw_init(struct term_buf* buf)
|
||||
{
|
||||
buf->width = tb_width();
|
||||
buf->height = tb_height();
|
||||
hostname(&buf->info_line);
|
||||
|
||||
u16 len_login = strlen(lang.login);
|
||||
u16 len_password = strlen(lang.password);
|
||||
|
||||
if (len_login > len_password)
|
||||
{
|
||||
buf->labels_max_len = len_login;
|
||||
}
|
||||
else
|
||||
{
|
||||
buf->labels_max_len = len_password;
|
||||
}
|
||||
|
||||
buf->box_height = 7 + (2 * config.margin_box_v);
|
||||
buf->box_width =
|
||||
(2 * config.margin_box_h)
|
||||
+ (config.input_len + 1)
|
||||
+ buf->labels_max_len;
|
||||
|
||||
#if defined(__linux__)
|
||||
buf->box_chars.left_up = 0x250c;
|
||||
buf->box_chars.left_down = 0x2514;
|
||||
buf->box_chars.right_up = 0x2510;
|
||||
buf->box_chars.right_down = 0x2518;
|
||||
buf->box_chars.top = 0x2500;
|
||||
buf->box_chars.bot = 0x2500;
|
||||
buf->box_chars.left = 0x2502;
|
||||
buf->box_chars.right = 0x2502;
|
||||
#else
|
||||
buf->box_chars.left_up = '+';
|
||||
buf->box_chars.left_down = '+';
|
||||
buf->box_chars.right_up = '+';
|
||||
buf->box_chars.right_down= '+';
|
||||
buf->box_chars.top = '-';
|
||||
buf->box_chars.bot = '-';
|
||||
buf->box_chars.left = '|';
|
||||
buf->box_chars.right = '|';
|
||||
#endif
|
||||
}
|
||||
|
||||
void draw_free(struct term_buf* buf)
|
||||
{
|
||||
if (config.animate)
|
||||
{
|
||||
free(buf->tmp_buf);
|
||||
}
|
||||
}
|
||||
|
||||
void draw_box(struct term_buf* buf)
|
||||
{
|
||||
u16 box_x = (buf->width - buf->box_width) / 2;
|
||||
u16 box_y = (buf->height - buf->box_height) / 2;
|
||||
u16 box_x2 = (buf->width + buf->box_width) / 2;
|
||||
u16 box_y2 = (buf->height + buf->box_height) / 2;
|
||||
buf->box_x = box_x;
|
||||
buf->box_y = box_y;
|
||||
|
||||
if (!config.hide_borders)
|
||||
{
|
||||
// corners
|
||||
tb_change_cell(
|
||||
box_x - 1,
|
||||
box_y - 1,
|
||||
buf->box_chars.left_up,
|
||||
config.fg,
|
||||
config.bg);
|
||||
tb_change_cell(
|
||||
box_x2,
|
||||
box_y - 1,
|
||||
buf->box_chars.right_up,
|
||||
config.fg,
|
||||
config.bg);
|
||||
tb_change_cell(
|
||||
box_x - 1,
|
||||
box_y2,
|
||||
buf->box_chars.left_down,
|
||||
config.fg,
|
||||
config.bg);
|
||||
tb_change_cell(
|
||||
box_x2,
|
||||
box_y2,
|
||||
buf->box_chars.right_down,
|
||||
config.fg,
|
||||
config.bg);
|
||||
|
||||
// top and bottom
|
||||
struct tb_cell c1 = {buf->box_chars.top, config.fg, config.bg};
|
||||
struct tb_cell c2 = {buf->box_chars.bot, config.fg, config.bg};
|
||||
|
||||
for (u8 i = 0; i < buf->box_width; ++i)
|
||||
{
|
||||
tb_put_cell(
|
||||
box_x + i,
|
||||
box_y - 1,
|
||||
&c1);
|
||||
tb_put_cell(
|
||||
box_x + i,
|
||||
box_y2,
|
||||
&c2);
|
||||
}
|
||||
|
||||
// left and right
|
||||
c1.ch = buf->box_chars.left;
|
||||
c2.ch = buf->box_chars.right;
|
||||
|
||||
for (u8 i = 0; i < buf->box_height; ++i)
|
||||
{
|
||||
tb_put_cell(
|
||||
box_x - 1,
|
||||
box_y + i,
|
||||
&c1);
|
||||
|
||||
tb_put_cell(
|
||||
box_x2,
|
||||
box_y + i,
|
||||
&c2);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.blank_box)
|
||||
{
|
||||
struct tb_cell blank = {' ', config.fg, config.bg};
|
||||
|
||||
for (u8 i = 0; i < buf->box_height; ++i)
|
||||
{
|
||||
for (u8 k = 0; k < buf->box_width; ++k)
|
||||
{
|
||||
tb_put_cell(
|
||||
box_x + k,
|
||||
box_y + i,
|
||||
&blank);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct tb_cell* strn_cell(char* s, u16 len) // throws
|
||||
{
|
||||
struct tb_cell* cells = malloc((sizeof (struct tb_cell)) * len);
|
||||
char* s2 = s;
|
||||
u32 c;
|
||||
|
||||
if (cells != NULL)
|
||||
{
|
||||
for (u16 i = 0; i < len; ++i)
|
||||
{
|
||||
if ((s2 - s) >= len)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
s2 += utf8_char_to_unicode(&c, s2);
|
||||
|
||||
cells[i].ch = c;
|
||||
cells[i].bg = config.bg;
|
||||
cells[i].fg = config.fg;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
}
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
struct tb_cell* str_cell(char* s) // throws
|
||||
{
|
||||
return strn_cell(s, strlen(s));
|
||||
}
|
||||
|
||||
void draw_labels(struct term_buf* buf) // throws
|
||||
{
|
||||
// login text
|
||||
struct tb_cell* login = str_cell(lang.login);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(
|
||||
buf->box_x + config.margin_box_h,
|
||||
buf->box_y + config.margin_box_v + 4,
|
||||
strlen(lang.login),
|
||||
1,
|
||||
login);
|
||||
free(login);
|
||||
}
|
||||
|
||||
// password text
|
||||
struct tb_cell* password = str_cell(lang.password);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(
|
||||
buf->box_x + config.margin_box_h,
|
||||
buf->box_y + config.margin_box_v + 6,
|
||||
strlen(lang.password),
|
||||
1,
|
||||
password);
|
||||
free(password);
|
||||
}
|
||||
|
||||
if (buf->info_line != NULL)
|
||||
{
|
||||
u16 len = strlen(buf->info_line);
|
||||
struct tb_cell* info_cell = str_cell(buf->info_line);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(
|
||||
buf->box_x + ((buf->box_width - len) / 2),
|
||||
buf->box_y + config.margin_box_v,
|
||||
len,
|
||||
1,
|
||||
info_cell);
|
||||
free(info_cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_f_commands()
|
||||
{
|
||||
struct tb_cell* f1 = str_cell(lang.f1);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(0, 0, strlen(lang.f1), 1, f1);
|
||||
free(f1);
|
||||
}
|
||||
|
||||
struct tb_cell* f2 = str_cell(lang.f2);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(strlen(lang.f1) + 1, 0, strlen(lang.f2), 1, f2);
|
||||
free(f2);
|
||||
}
|
||||
}
|
||||
|
||||
void draw_lock_state(struct term_buf* buf)
|
||||
{
|
||||
// get values
|
||||
int fd = open(config.console_dev, O_RDONLY);
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
buf->info_line = lang.err_console_dev;
|
||||
return;
|
||||
}
|
||||
|
||||
bool numlock_on;
|
||||
bool capslock_on;
|
||||
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
int led;
|
||||
ioctl(fd, KDGETLED, &led);
|
||||
numlock_on = led & LED_NUM;
|
||||
capslock_on = led & LED_CAP;
|
||||
#else // linux
|
||||
char led;
|
||||
ioctl(fd, KDGKBLED, &led);
|
||||
numlock_on = led & K_NUMLOCK;
|
||||
capslock_on = led & K_CAPSLOCK;
|
||||
#endif
|
||||
|
||||
close(fd);
|
||||
|
||||
// print text
|
||||
u16 pos_x = buf->width - strlen(lang.numlock);
|
||||
|
||||
if (numlock_on)
|
||||
{
|
||||
struct tb_cell* numlock = str_cell(lang.numlock);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(pos_x, 0, strlen(lang.numlock), 1, numlock);
|
||||
free(numlock);
|
||||
}
|
||||
}
|
||||
|
||||
pos_x -= strlen(lang.capslock) + 1;
|
||||
|
||||
if (capslock_on)
|
||||
{
|
||||
struct tb_cell* capslock = str_cell(lang.capslock);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(pos_x, 0, strlen(lang.capslock), 1, capslock);
|
||||
free(capslock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_desktop(struct desktop* target)
|
||||
{
|
||||
u16 len = strlen(target->list[target->cur]);
|
||||
|
||||
if (len > (target->visible_len - 3))
|
||||
{
|
||||
len = target->visible_len - 3;
|
||||
}
|
||||
|
||||
tb_change_cell(
|
||||
target->x,
|
||||
target->y,
|
||||
'<',
|
||||
config.fg,
|
||||
config.bg);
|
||||
|
||||
tb_change_cell(
|
||||
target->x + target->visible_len - 1,
|
||||
target->y,
|
||||
'>',
|
||||
config.fg,
|
||||
config.bg);
|
||||
|
||||
for (u16 i = 0; i < len; ++ i)
|
||||
{
|
||||
tb_change_cell(
|
||||
target->x + i + 2,
|
||||
target->y,
|
||||
target->list[target->cur][i],
|
||||
config.fg,
|
||||
config.bg);
|
||||
}
|
||||
}
|
||||
|
||||
void draw_input(struct text* input)
|
||||
{
|
||||
u16 len = strlen(input->text);
|
||||
u16 visible_len = input->visible_len;
|
||||
|
||||
if (len > visible_len)
|
||||
{
|
||||
len = visible_len;
|
||||
}
|
||||
|
||||
struct tb_cell* cells = strn_cell(input->visible_start, len);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(input->x, input->y, len, 1, cells);
|
||||
free(cells);
|
||||
|
||||
struct tb_cell c1 = {' ', config.fg, config.bg};
|
||||
|
||||
for (u16 i = input->end - input->visible_start; i < visible_len; ++i)
|
||||
{
|
||||
tb_put_cell(
|
||||
input->x + i,
|
||||
input->y,
|
||||
&c1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_input_mask(struct text* input)
|
||||
{
|
||||
u16 len = strlen(input->text);
|
||||
u16 visible_len = input->visible_len;
|
||||
|
||||
if (len > visible_len)
|
||||
{
|
||||
len = visible_len;
|
||||
}
|
||||
|
||||
struct tb_cell c1 = {config.asterisk, config.fg, config.bg};
|
||||
struct tb_cell c2 = {' ', config.fg, config.bg};
|
||||
|
||||
for (u16 i = 0; i < visible_len; ++i)
|
||||
{
|
||||
if (input->visible_start + i < input->end)
|
||||
{
|
||||
tb_put_cell(
|
||||
input->x + i,
|
||||
input->y,
|
||||
&c1);
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_put_cell(
|
||||
input->x + i,
|
||||
input->y,
|
||||
&c2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void position_input(
|
||||
struct term_buf* buf,
|
||||
struct desktop* desktop,
|
||||
struct text* login,
|
||||
struct text* password)
|
||||
{
|
||||
u16 x = buf->box_x + config.margin_box_h + buf->labels_max_len + 1;
|
||||
i32 len = buf->box_x + buf->box_width - config.margin_box_h - x;
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
desktop->x = x;
|
||||
desktop->y = buf->box_y + config.margin_box_v + 2;
|
||||
desktop->visible_len = len;
|
||||
|
||||
login->x = x;
|
||||
login->y = buf->box_y + config.margin_box_v + 4;
|
||||
login->visible_len = len;
|
||||
|
||||
password->x = x;
|
||||
password->y = buf->box_y + config.margin_box_v + 6;
|
||||
password->visible_len = len;
|
||||
}
|
||||
|
||||
static void doom_init(struct term_buf* buf)
|
||||
{
|
||||
buf->init_width = buf->width;
|
||||
buf->init_height = buf->height;
|
||||
|
||||
u16 tmp_len = buf->width * buf->height;
|
||||
buf->tmp_buf = malloc(tmp_len);
|
||||
tmp_len -= buf->width;
|
||||
|
||||
if (buf->tmp_buf == NULL)
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
}
|
||||
|
||||
memset(buf->tmp_buf, 0, tmp_len);
|
||||
memset(buf->tmp_buf + tmp_len, DOOM_STEPS - 1, buf->width);
|
||||
}
|
||||
|
||||
void animate_init(struct term_buf* buf)
|
||||
{
|
||||
if (config.animate)
|
||||
{
|
||||
switch(config.animation)
|
||||
{
|
||||
default:
|
||||
{
|
||||
doom_init(buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void doom(struct term_buf* term_buf)
|
||||
{
|
||||
static struct tb_cell fire[DOOM_STEPS] =
|
||||
{
|
||||
{' ', 9, 0}, // default
|
||||
{0x2591, 2, 0}, // red
|
||||
{0x2592, 2, 0}, // red
|
||||
{0x2593, 2, 0}, // red
|
||||
{0x2588, 2, 0}, // red
|
||||
{0x2591, 4, 2}, // yellow
|
||||
{0x2592, 4, 2}, // yellow
|
||||
{0x2593, 4, 2}, // yellow
|
||||
{0x2588, 4, 2}, // yellow
|
||||
{0x2591, 8, 4}, // white
|
||||
{0x2592, 8, 4}, // white
|
||||
{0x2593, 8, 4}, // white
|
||||
{0x2588, 8, 4}, // white
|
||||
};
|
||||
|
||||
u16 src;
|
||||
u16 random;
|
||||
u16 dst;
|
||||
|
||||
u16 w = term_buf->init_width;
|
||||
u8* tmp = term_buf->tmp_buf;
|
||||
|
||||
if ((term_buf->width != term_buf->init_width) || (term_buf->height != term_buf->init_height))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
struct tb_cell* buf = tb_cell_buffer();
|
||||
|
||||
for (u16 x = 0; x < w; ++x)
|
||||
{
|
||||
for (u16 y = 1; y < term_buf->init_height; ++y)
|
||||
{
|
||||
src = y * w + x;
|
||||
random = ((rand() % 7) & 3);
|
||||
dst = src - random + 1;
|
||||
|
||||
if (w > dst)
|
||||
{
|
||||
dst = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst -= w;
|
||||
}
|
||||
|
||||
tmp[dst] = tmp[src] - (random & 1);
|
||||
|
||||
if (tmp[dst] > 12)
|
||||
{
|
||||
tmp[dst] = 0;
|
||||
}
|
||||
|
||||
buf[dst] = fire[tmp[dst]];
|
||||
buf[src] = fire[tmp[src]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void animate(struct term_buf* buf)
|
||||
{
|
||||
buf->width = tb_width();
|
||||
buf->height = tb_height();
|
||||
|
||||
if (config.animate)
|
||||
{
|
||||
switch(config.animation)
|
||||
{
|
||||
default:
|
||||
{
|
||||
doom(buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cascade(struct term_buf* term_buf, u8* fails)
|
||||
{
|
||||
u16 width = term_buf->width;
|
||||
u16 height = term_buf->height;
|
||||
|
||||
struct tb_cell* buf = tb_cell_buffer();
|
||||
bool changes = false;
|
||||
char c_under;
|
||||
char c;
|
||||
|
||||
for (int i = height - 2; i >= 0; --i)
|
||||
{
|
||||
for (int k = 0; k < width; ++k)
|
||||
{
|
||||
c = buf[i * width + k].ch;
|
||||
|
||||
if (isspace(c))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
c_under = buf[(i + 1) * width + k].ch;
|
||||
|
||||
if (!isspace(c_under))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!changes)
|
||||
{
|
||||
changes = true;
|
||||
}
|
||||
|
||||
if ((rand() % 10) > 7)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
buf[(i + 1) * width + k] = buf[i * width + k];
|
||||
buf[i * width + k].ch = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
// stop force-updating
|
||||
if (!changes)
|
||||
{
|
||||
sleep(7);
|
||||
*fails = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// force-update
|
||||
return true;
|
||||
}
|
||||
63
src/draw.h
Normal file
63
src/draw.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#ifndef H_LY_DRAW
|
||||
#define H_LY_DRAW
|
||||
|
||||
#include "termbox.h"
|
||||
#include "ctypes.h"
|
||||
|
||||
#include "inputs.h"
|
||||
|
||||
struct box
|
||||
{
|
||||
u32 left_up;
|
||||
u32 left_down;
|
||||
u32 right_up;
|
||||
u32 right_down;
|
||||
u32 top;
|
||||
u32 bot;
|
||||
u32 left;
|
||||
u32 right;
|
||||
};
|
||||
|
||||
struct term_buf
|
||||
{
|
||||
u16 width;
|
||||
u16 height;
|
||||
u16 init_width;
|
||||
u16 init_height;
|
||||
|
||||
struct box box_chars;
|
||||
char* info_line;
|
||||
u16 labels_max_len;
|
||||
u16 box_x;
|
||||
u16 box_y;
|
||||
u16 box_width;
|
||||
u16 box_height;
|
||||
|
||||
u8* tmp_buf;
|
||||
};
|
||||
|
||||
void draw_init(struct term_buf* buf);
|
||||
void draw_free(struct term_buf* buf);
|
||||
void draw_box(struct term_buf* buf);
|
||||
|
||||
struct tb_cell* strn_cell(char* s, u16 len);
|
||||
struct tb_cell* str_cell(char* s);
|
||||
|
||||
void draw_labels(struct term_buf* buf);
|
||||
void draw_f_commands();
|
||||
void draw_lock_state(struct term_buf* buf);
|
||||
void draw_desktop(struct desktop* target);
|
||||
void draw_input(struct text* input);
|
||||
void draw_input_mask(struct text* input);
|
||||
|
||||
void position_input(
|
||||
struct term_buf* buf,
|
||||
struct desktop* desktop,
|
||||
struct text* login,
|
||||
struct text* password);
|
||||
|
||||
void animate_init(struct term_buf* buf);
|
||||
void animate(struct term_buf* buf);
|
||||
bool cascade(struct term_buf* buf, u8* fails);
|
||||
|
||||
#endif
|
||||
@@ -1,31 +0,0 @@
|
||||
pub const Animation = enum {
|
||||
none,
|
||||
doom,
|
||||
matrix,
|
||||
colormix,
|
||||
};
|
||||
|
||||
pub const DisplayServer = enum {
|
||||
wayland,
|
||||
shell,
|
||||
xinitrc,
|
||||
x11,
|
||||
};
|
||||
|
||||
pub const Input = enum {
|
||||
info_line,
|
||||
session,
|
||||
login,
|
||||
password,
|
||||
};
|
||||
|
||||
pub const ViMode = enum {
|
||||
normal,
|
||||
insert,
|
||||
};
|
||||
|
||||
pub const Bigclock = enum {
|
||||
none,
|
||||
en,
|
||||
fa,
|
||||
};
|
||||
268
src/inputs.c
Normal file
268
src/inputs.c
Normal file
@@ -0,0 +1,268 @@
|
||||
#include "dragonfail.h"
|
||||
#include "termbox.h"
|
||||
#include "ctypes.h"
|
||||
|
||||
#include "inputs.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
void handle_desktop(void* input_struct, struct tb_event* event)
|
||||
{
|
||||
struct desktop* target = (struct desktop*) input_struct;
|
||||
|
||||
if ((event != NULL) && (event->type == TB_EVENT_KEY))
|
||||
{
|
||||
if (event->key == TB_KEY_ARROW_LEFT)
|
||||
{
|
||||
input_desktop_right(target);
|
||||
}
|
||||
else if (event->key == TB_KEY_ARROW_RIGHT)
|
||||
{
|
||||
input_desktop_left(target);
|
||||
}
|
||||
}
|
||||
|
||||
tb_set_cursor(target->x + 2, target->y);
|
||||
}
|
||||
|
||||
void handle_text(void* input_struct, struct tb_event* event)
|
||||
{
|
||||
struct text* target = (struct text*) input_struct;
|
||||
|
||||
if ((event != NULL) && (event->type == TB_EVENT_KEY))
|
||||
{
|
||||
if (event->key == TB_KEY_ARROW_LEFT)
|
||||
{
|
||||
input_text_left(target);
|
||||
}
|
||||
else if (event->key == TB_KEY_ARROW_RIGHT)
|
||||
{
|
||||
input_text_right(target);
|
||||
}
|
||||
else if (event->key == TB_KEY_DELETE)
|
||||
{
|
||||
input_text_delete(target);
|
||||
}
|
||||
else if ((event->key == TB_KEY_BACKSPACE)
|
||||
|| (event->key == TB_KEY_BACKSPACE2))
|
||||
{
|
||||
input_text_backspace(target);
|
||||
}
|
||||
else if (((event->ch > 31) && (event->ch < 127))
|
||||
|| (event->key == TB_KEY_SPACE))
|
||||
{
|
||||
char buf[7] = {0};
|
||||
|
||||
if (event->key == TB_KEY_SPACE)
|
||||
{
|
||||
buf[0] = ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
utf8_unicode_to_char(buf, event->ch);
|
||||
}
|
||||
|
||||
input_text_write(target, buf[0]);
|
||||
}
|
||||
}
|
||||
|
||||
tb_set_cursor(
|
||||
target->x + (target->cur - target->visible_start),
|
||||
target->y);
|
||||
}
|
||||
|
||||
void input_desktop(struct desktop* target)
|
||||
{
|
||||
target->list = NULL;
|
||||
target->cmd = NULL;
|
||||
target->display_server = NULL;
|
||||
target->cur = 0;
|
||||
target->len = 0;
|
||||
|
||||
input_desktop_add(target, strdup(lang.shell), strdup(""), DS_SHELL);
|
||||
input_desktop_add(target, strdup(lang.xinitrc), strdup("~/.xinitrc"), DS_XINITRC);
|
||||
#if 0
|
||||
input_desktop_add(target, strdup(lang.wayland), strdup(""), DS_WAYLAND);
|
||||
#endif
|
||||
}
|
||||
|
||||
void input_text(struct text* target, u64 len)
|
||||
{
|
||||
target->text = malloc(len + 1);
|
||||
|
||||
if (target->text == NULL)
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
int ok = mlock(target->text, len + 1);
|
||||
|
||||
if (ok < 0)
|
||||
{
|
||||
dgn_throw(DGN_MLOCK);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(target->text, 0, len + 1);
|
||||
}
|
||||
|
||||
target->cur = target->text;
|
||||
target->end = target->text;
|
||||
target->visible_start = target->text;
|
||||
target->len = len;
|
||||
target->x = 0;
|
||||
target->y = 0;
|
||||
}
|
||||
|
||||
void input_desktop_free(struct desktop* target)
|
||||
{
|
||||
if (target != NULL)
|
||||
{
|
||||
for (u16 i = 0; i < target->len; ++i)
|
||||
{
|
||||
if (target->list[i] != NULL)
|
||||
{
|
||||
free(target->list[i]);
|
||||
}
|
||||
|
||||
if (target->cmd[i] != NULL)
|
||||
{
|
||||
free(target->cmd[i]);
|
||||
}
|
||||
}
|
||||
|
||||
free(target->list);
|
||||
free(target->cmd);
|
||||
free(target->display_server);
|
||||
}
|
||||
}
|
||||
|
||||
void input_text_free(struct text* target)
|
||||
{
|
||||
memset(target->text, 0, target->len);
|
||||
munlock(target->text, target->len + 1);
|
||||
free(target->text);
|
||||
}
|
||||
|
||||
void input_desktop_right(struct desktop* target)
|
||||
{
|
||||
++(target->cur);
|
||||
|
||||
if (target->cur >= target->len)
|
||||
{
|
||||
target->cur = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void input_desktop_left(struct desktop* target)
|
||||
{
|
||||
--(target->cur);
|
||||
|
||||
if (target->cur >= target->len)
|
||||
{
|
||||
target->cur = target->len - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void input_desktop_add(
|
||||
struct desktop* target,
|
||||
char* name,
|
||||
char* cmd,
|
||||
enum display_server display_server)
|
||||
{
|
||||
++(target->len);
|
||||
target->list = realloc(target->list, target->len * (sizeof (char*)));
|
||||
target->cmd = realloc(target->cmd, target->len * (sizeof (char*)));
|
||||
target->display_server = realloc(
|
||||
target->display_server,
|
||||
target->len * (sizeof (enum display_server)));
|
||||
target->cur = target->len - 1;
|
||||
|
||||
if ((target->list == NULL)
|
||||
|| (target->cmd == NULL)
|
||||
|| (target->display_server == NULL))
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
return;
|
||||
}
|
||||
|
||||
target->list[target->cur] = name;
|
||||
target->cmd[target->cur] = cmd;
|
||||
target->display_server[target->cur] = display_server;
|
||||
}
|
||||
|
||||
void input_text_right(struct text* target)
|
||||
{
|
||||
if (target->cur < target->end)
|
||||
{
|
||||
++(target->cur);
|
||||
|
||||
if ((target->cur - target->visible_start) > target->visible_len)
|
||||
{
|
||||
++(target->visible_start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void input_text_left(struct text* target)
|
||||
{
|
||||
if (target->cur > target->text)
|
||||
{
|
||||
--(target->cur);
|
||||
|
||||
if ((target->cur - target->visible_start) < 0)
|
||||
{
|
||||
--(target->visible_start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void input_text_write(struct text* target, char ascii)
|
||||
{
|
||||
if (ascii <= 0)
|
||||
{
|
||||
return; // unices do not support usernames and passwords other than ascii
|
||||
}
|
||||
|
||||
if ((target->end - target->text + 1) < target->len)
|
||||
{
|
||||
// moves the text to the right to add space for the new ascii char
|
||||
memcpy(target->cur + 1, target->cur, target->end - target->cur);
|
||||
++(target->end);
|
||||
// adds the new char and moves the cursor to the right
|
||||
*(target->cur) = ascii;
|
||||
input_text_right(target);
|
||||
}
|
||||
}
|
||||
|
||||
void input_text_delete(struct text* target)
|
||||
{
|
||||
if (target->cur < target->end)
|
||||
{
|
||||
// moves the text on the right to overwrite the currently pointed char
|
||||
memcpy(target->cur, target->cur + 1, target->end - target->cur + 1);
|
||||
--(target->end);
|
||||
}
|
||||
}
|
||||
|
||||
void input_text_backspace(struct text* target)
|
||||
{
|
||||
if (target->cur > target->text)
|
||||
{
|
||||
input_text_left(target);
|
||||
input_text_delete(target);
|
||||
}
|
||||
}
|
||||
|
||||
void input_text_clear(struct text* target)
|
||||
{
|
||||
memset(target->text, 0, target->len + 1);
|
||||
target->cur = target->text;
|
||||
target->end = target->text;
|
||||
target->visible_start = target->text;
|
||||
}
|
||||
55
src/inputs.h
Normal file
55
src/inputs.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef H_LY_INPUTS
|
||||
#define H_LY_INPUTS
|
||||
|
||||
#include "termbox.h"
|
||||
#include "ctypes.h"
|
||||
|
||||
enum display_server {DS_WAYLAND, DS_SHELL, DS_XINITRC, DS_XORG};
|
||||
|
||||
struct text
|
||||
{
|
||||
char* text;
|
||||
char* end;
|
||||
i64 len;
|
||||
char* cur;
|
||||
char* visible_start;
|
||||
u16 visible_len;
|
||||
|
||||
u16 x;
|
||||
u16 y;
|
||||
};
|
||||
|
||||
struct desktop
|
||||
{
|
||||
char** list;
|
||||
char** cmd;
|
||||
enum display_server* display_server;
|
||||
|
||||
u16 cur;
|
||||
u16 len;
|
||||
u16 visible_len;
|
||||
u16 x;
|
||||
u16 y;
|
||||
};
|
||||
|
||||
void handle_desktop(void* input_struct, struct tb_event* event);
|
||||
void handle_text(void* input_struct, struct tb_event* event);
|
||||
void input_desktop(struct desktop* target);
|
||||
void input_text(struct text* target, u64 len);
|
||||
void input_desktop_free(struct desktop* target);
|
||||
void input_text_free(struct text* target);
|
||||
void input_desktop_right(struct desktop* target);
|
||||
void input_desktop_left(struct desktop* target);
|
||||
void input_desktop_add(
|
||||
struct desktop* target,
|
||||
char* name,
|
||||
char* cmd,
|
||||
enum display_server display_server);
|
||||
void input_text_right(struct text* target);
|
||||
void input_text_left(struct text* target);
|
||||
void input_text_write(struct text* target, char ascii);
|
||||
void input_text_delete(struct text* target);
|
||||
void input_text_backspace(struct text* target);
|
||||
void input_text_clear(struct text* target);
|
||||
|
||||
#endif
|
||||
109
src/interop.zig
109
src/interop.zig
@@ -1,109 +0,0 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const termbox = @import("termbox2");
|
||||
|
||||
pub const pam = @cImport({
|
||||
@cInclude("security/pam_appl.h");
|
||||
});
|
||||
|
||||
pub const utmp = @cImport({
|
||||
@cInclude("utmpx.h");
|
||||
});
|
||||
|
||||
// Exists for X11 support only
|
||||
pub const xcb = @cImport({
|
||||
@cInclude("xcb/xcb.h");
|
||||
});
|
||||
|
||||
pub const unistd = @cImport({
|
||||
@cInclude("unistd.h");
|
||||
});
|
||||
|
||||
pub const time = @cImport({
|
||||
@cInclude("time.h");
|
||||
});
|
||||
|
||||
pub const system_time = @cImport({
|
||||
@cInclude("sys/time.h");
|
||||
});
|
||||
|
||||
pub const stdlib = @cImport({
|
||||
@cInclude("stdlib.h");
|
||||
});
|
||||
|
||||
pub const pwd = @cImport({
|
||||
@cInclude("pwd.h");
|
||||
// We include a FreeBSD-specific header here since login_cap.h references
|
||||
// the passwd struct directly, so we can't import it separately'
|
||||
if (builtin.os.tag == .freebsd) @cInclude("login_cap.h");
|
||||
});
|
||||
|
||||
pub const grp = @cImport({
|
||||
@cInclude("grp.h");
|
||||
});
|
||||
|
||||
// BSD-specific headers
|
||||
pub const kbio = @cImport({
|
||||
@cInclude("sys/kbio.h");
|
||||
});
|
||||
|
||||
// Linux-specific headers
|
||||
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 tm_info = time.localtime(&timer);
|
||||
const len = time.strftime(buf, buf.len, format, tm_info);
|
||||
|
||||
return buf[0..len];
|
||||
}
|
||||
|
||||
pub fn switchTty(console_dev: []const u8, tty: u8) !void {
|
||||
const fd = try std.posix.open(console_dev, .{ .ACCMODE = .WRONLY }, 0);
|
||||
defer std.posix.close(fd);
|
||||
|
||||
_ = 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,
|
||||
capslock: bool,
|
||||
} {
|
||||
const fd = try std.posix.open(console_dev, .{ .ACCMODE = .RDONLY }, 0);
|
||||
defer std.posix.close(fd);
|
||||
|
||||
var led: LedState = undefined;
|
||||
_ = std.c.ioctl(fd, get_led_state, &led);
|
||||
|
||||
return .{
|
||||
.numlock = (led & numlock_led) != 0,
|
||||
.capslock = (led & capslock_led) != 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setNumlock(val: bool) !void {
|
||||
var led: LedState = undefined;
|
||||
_ = std.c.ioctl(0, get_led_state, &led);
|
||||
|
||||
const numlock = (led & numlock_led) != 0;
|
||||
if (numlock != val) {
|
||||
const status = std.c.ioctl(std.posix.STDIN_FILENO, set_led_state, led ^ numlock_led);
|
||||
if (status != 0) return error.FailedToSetNumlock;
|
||||
}
|
||||
}
|
||||
680
src/login.c
Normal file
680
src/login.c
Normal file
@@ -0,0 +1,680 @@
|
||||
#include "dragonfail.h"
|
||||
#include "termbox.h"
|
||||
|
||||
#include "inputs.h"
|
||||
#include "draw.h"
|
||||
#include "utils.h"
|
||||
#include "config.h"
|
||||
#include "login.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <security/pam_appl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <utmp.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
int get_free_display()
|
||||
{
|
||||
char xlock[1024];
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < 200; ++i)
|
||||
{
|
||||
snprintf(xlock, 1024, "/tmp/.X%d-lock", i);
|
||||
|
||||
if (access(xlock, F_OK) == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void reset_terminal(struct passwd* pwd)
|
||||
{
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
execl(pwd->pw_shell, pwd->pw_shell, "-c", config.term_reset_cmd, NULL);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
}
|
||||
|
||||
int login_conv(
|
||||
int num_msg,
|
||||
const struct pam_message** msg,
|
||||
struct pam_response** resp,
|
||||
void* appdata_ptr)
|
||||
{
|
||||
*resp = calloc(num_msg, sizeof (struct pam_response));
|
||||
|
||||
if (*resp == NULL)
|
||||
{
|
||||
return PAM_BUF_ERR;
|
||||
}
|
||||
|
||||
char* username;
|
||||
char* password;
|
||||
int ok = PAM_SUCCESS;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_msg; ++i)
|
||||
{
|
||||
switch (msg[i]->msg_style)
|
||||
{
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
{
|
||||
username = ((char**) appdata_ptr)[0];
|
||||
(*resp)[i].resp = strdup(username);
|
||||
break;
|
||||
}
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
{
|
||||
password = ((char**) appdata_ptr)[1];
|
||||
(*resp)[i].resp = strdup(password);
|
||||
break;
|
||||
}
|
||||
case PAM_ERROR_MSG:
|
||||
{
|
||||
ok = PAM_CONV_ERR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
for (i = 0; i < num_msg; ++i)
|
||||
{
|
||||
if ((*resp)[i].resp == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
free((*resp)[i].resp);
|
||||
(*resp)[i].resp = NULL;
|
||||
}
|
||||
|
||||
free(*resp);
|
||||
*resp = NULL;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void pam_diagnose(int error, struct term_buf* buf)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case PAM_ACCT_EXPIRED:
|
||||
{
|
||||
buf->info_line = lang.err_pam_acct_expired;
|
||||
break;
|
||||
}
|
||||
case PAM_AUTH_ERR:
|
||||
{
|
||||
buf->info_line = lang.err_pam_auth;
|
||||
break;
|
||||
}
|
||||
case PAM_AUTHINFO_UNAVAIL:
|
||||
{
|
||||
buf->info_line = lang.err_pam_authinfo_unavail;
|
||||
break;
|
||||
}
|
||||
case PAM_BUF_ERR:
|
||||
{
|
||||
buf->info_line = lang.err_pam_buf;
|
||||
break;
|
||||
}
|
||||
case PAM_CRED_ERR:
|
||||
{
|
||||
buf->info_line = lang.err_pam_cred_err;
|
||||
break;
|
||||
}
|
||||
case PAM_CRED_EXPIRED:
|
||||
{
|
||||
buf->info_line = lang.err_pam_cred_expired;
|
||||
break;
|
||||
}
|
||||
case PAM_CRED_INSUFFICIENT:
|
||||
{
|
||||
buf->info_line = lang.err_pam_cred_insufficient;
|
||||
break;
|
||||
}
|
||||
case PAM_CRED_UNAVAIL:
|
||||
{
|
||||
buf->info_line = lang.err_pam_cred_unavail;
|
||||
break;
|
||||
}
|
||||
case PAM_MAXTRIES:
|
||||
{
|
||||
buf->info_line = lang.err_pam_maxtries;
|
||||
break;
|
||||
}
|
||||
case PAM_NEW_AUTHTOK_REQD:
|
||||
{
|
||||
buf->info_line = lang.err_pam_authok_reqd;
|
||||
break;
|
||||
}
|
||||
case PAM_PERM_DENIED:
|
||||
{
|
||||
buf->info_line = lang.err_pam_perm_denied;
|
||||
break;
|
||||
}
|
||||
case PAM_SESSION_ERR:
|
||||
{
|
||||
buf->info_line = lang.err_pam_session;
|
||||
break;
|
||||
}
|
||||
case PAM_SYSTEM_ERR:
|
||||
{
|
||||
buf->info_line = lang.err_pam_sys;
|
||||
break;
|
||||
}
|
||||
case PAM_USER_UNKNOWN:
|
||||
{
|
||||
buf->info_line = lang.err_pam_user_unknown;
|
||||
break;
|
||||
}
|
||||
case PAM_ABORT:
|
||||
default:
|
||||
{
|
||||
buf->info_line = lang.err_pam_abort;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dgn_throw(DGN_PAM);
|
||||
}
|
||||
|
||||
void env_init(struct passwd* pwd)
|
||||
{
|
||||
extern char** environ;
|
||||
char* term = getenv("TERM");
|
||||
char* lang = getenv("LANG");
|
||||
// clean env
|
||||
environ[0] = NULL;
|
||||
|
||||
if (term != NULL)
|
||||
{
|
||||
setenv("TERM", term, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
setenv("TERM", "linux", 1);
|
||||
}
|
||||
|
||||
setenv("HOME", pwd->pw_dir, 1);
|
||||
setenv("PWD", pwd->pw_dir, 1);
|
||||
setenv("SHELL", pwd->pw_shell, 1);
|
||||
setenv("USER", pwd->pw_name, 1);
|
||||
setenv("LOGNAME", pwd->pw_name, 1);
|
||||
setenv("LANG", lang, 1);
|
||||
|
||||
// Set PATH if specified in the configuration
|
||||
if (strlen(config.path))
|
||||
{
|
||||
int ok = setenv("PATH", config.path, 1);
|
||||
|
||||
if (ok != 0)
|
||||
{
|
||||
dgn_throw(DGN_PATH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void env_xdg(const char* tty_id, const enum display_server display_server)
|
||||
{
|
||||
char user[15];
|
||||
snprintf(user, 15, "/run/user/%d", getuid());
|
||||
setenv("XDG_RUNTIME_DIR", user, 0);
|
||||
setenv("XDG_SESSION_CLASS", "user", 0);
|
||||
setenv("XDG_SEAT", "seat0", 0);
|
||||
setenv("XDG_VTNR", tty_id, 0);
|
||||
|
||||
switch (display_server)
|
||||
{
|
||||
case DS_WAYLAND:
|
||||
{
|
||||
setenv("XDG_SESSION_TYPE", "wayland", 1);
|
||||
break;
|
||||
}
|
||||
case DS_SHELL:
|
||||
{
|
||||
setenv("XDG_SESSION_TYPE", "tty", 0);
|
||||
break;
|
||||
}
|
||||
case DS_XINITRC:
|
||||
case DS_XORG:
|
||||
{
|
||||
setenv("XDG_SESSION_TYPE", "x11", 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_utmp_entry(
|
||||
struct utmp *entry,
|
||||
char *username,
|
||||
pid_t display_pid
|
||||
) {
|
||||
entry->ut_type = USER_PROCESS;
|
||||
entry->ut_pid = display_pid;
|
||||
strcpy(entry->ut_line, ttyname(STDIN_FILENO) + strlen("/dev/"));
|
||||
|
||||
/* only correct for ptys named /dev/tty[pqr][0-9a-z] */
|
||||
strcpy(entry->ut_id, ttyname(STDIN_FILENO) + strlen("/dev/tty"));
|
||||
|
||||
time((long int *) &entry->ut_time);
|
||||
|
||||
strncpy(entry->ut_user, username, UT_NAMESIZE);
|
||||
memset(entry->ut_host, 0, UT_HOSTSIZE);
|
||||
entry->ut_addr = 0;
|
||||
setutent();
|
||||
|
||||
pututline(entry);
|
||||
}
|
||||
|
||||
void remove_utmp_entry(struct utmp *entry) {
|
||||
entry->ut_type = DEAD_PROCESS;
|
||||
memset(entry->ut_line, 0, UT_LINESIZE);
|
||||
entry->ut_time = 0;
|
||||
memset(entry->ut_user, 0, UT_NAMESIZE);
|
||||
setutent();
|
||||
pututline(entry);
|
||||
endutent();
|
||||
}
|
||||
|
||||
void xauth(const char* display_name, const char* shell, const char* dir)
|
||||
{
|
||||
char xauthority[256];
|
||||
snprintf(xauthority, 256, "%s/%s", dir, ".lyxauth");
|
||||
setenv("XAUTHORITY", xauthority, 1);
|
||||
setenv("DISPLAY", display_name, 1);
|
||||
|
||||
FILE* fp = fopen(xauthority, "ab+");
|
||||
|
||||
if (fp != NULL)
|
||||
{
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
char cmd[1024];
|
||||
snprintf(
|
||||
cmd,
|
||||
1024,
|
||||
"%s add %s . `%s`",
|
||||
config.xauth_cmd,
|
||||
display_name,
|
||||
config.mcookie_cmd);
|
||||
execl(shell, shell, "-c", cmd, NULL);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
}
|
||||
|
||||
void xorg(
|
||||
struct passwd* pwd,
|
||||
const char* vt,
|
||||
const char* desktop_cmd)
|
||||
{
|
||||
// generate xauthority file
|
||||
const char* xauth_dir = getenv("XDG_CONFIG_HOME");
|
||||
|
||||
if ((xauth_dir == NULL) || (*xauth_dir == '\0'))
|
||||
{
|
||||
xauth_dir = pwd->pw_dir;
|
||||
}
|
||||
|
||||
char display_name[4];
|
||||
|
||||
snprintf(display_name, 3, ":%d", get_free_display());
|
||||
xauth(display_name, pwd->pw_shell, xauth_dir);
|
||||
|
||||
// start xorg
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
char x_cmd[1024];
|
||||
snprintf(
|
||||
x_cmd,
|
||||
1024,
|
||||
"%s %s %s",
|
||||
config.x_cmd,
|
||||
display_name,
|
||||
vt);
|
||||
execl(pwd->pw_shell, pwd->pw_shell, "-c", x_cmd, NULL);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int ok;
|
||||
xcb_connection_t* xcb;
|
||||
|
||||
do
|
||||
{
|
||||
xcb = xcb_connect(NULL, NULL);
|
||||
ok = xcb_connection_has_error(xcb);
|
||||
kill(pid, 0);
|
||||
}
|
||||
while((ok != 0) && (errno != ESRCH));
|
||||
|
||||
if (ok != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pid_t xorg_pid = fork();
|
||||
|
||||
if (xorg_pid == 0)
|
||||
{
|
||||
char de_cmd[1024];
|
||||
snprintf(
|
||||
de_cmd,
|
||||
1024,
|
||||
"%s %s",
|
||||
config.x_cmd_setup,
|
||||
desktop_cmd);
|
||||
execl(pwd->pw_shell, pwd->pw_shell, "-c", de_cmd, NULL);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int status;
|
||||
waitpid(xorg_pid, &status, 0);
|
||||
xcb_disconnect(xcb);
|
||||
kill(pid, 0);
|
||||
|
||||
if (errno != ESRCH)
|
||||
{
|
||||
kill(pid, SIGTERM);
|
||||
waitpid(pid, &status, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void wayland(
|
||||
struct passwd* pwd,
|
||||
const char* desktop_cmd)
|
||||
{
|
||||
|
||||
char cmd[1024];
|
||||
snprintf(cmd, 1024, "%s %s", config.wayland_cmd, desktop_cmd);
|
||||
execl(pwd->pw_shell, pwd->pw_shell, "-c", cmd, NULL);
|
||||
}
|
||||
|
||||
void shell(struct passwd* pwd)
|
||||
{
|
||||
const char* pos = strrchr(pwd->pw_shell, '/');
|
||||
char args[1024];
|
||||
args[0] = '-';
|
||||
|
||||
if (pos != NULL)
|
||||
{
|
||||
pos = pos + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = pwd->pw_shell;
|
||||
}
|
||||
|
||||
strncpy(args + 1, pos, 1023);
|
||||
execl(pwd->pw_shell, args, NULL);
|
||||
}
|
||||
|
||||
// pam_do performs the pam action specified in pam_action
|
||||
// on pam_action fail, call diagnose and end pam session
|
||||
int pam_do(
|
||||
int (pam_action)(struct pam_handle *, int),
|
||||
struct pam_handle *handle,
|
||||
int flags,
|
||||
struct term_buf *buf)
|
||||
{
|
||||
int status = pam_action(handle, flags);
|
||||
|
||||
if (status != PAM_SUCCESS) {
|
||||
pam_diagnose(status, buf);
|
||||
pam_end(handle, status);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void auth(
|
||||
struct desktop* desktop,
|
||||
struct text* login,
|
||||
struct text* password,
|
||||
struct term_buf* buf)
|
||||
{
|
||||
int ok;
|
||||
|
||||
// open pam session
|
||||
const char* creds[2] = {login->text, password->text};
|
||||
struct pam_conv conv = {login_conv, creds};
|
||||
struct pam_handle* handle;
|
||||
|
||||
ok = pam_start(config.service_name, NULL, &conv, &handle);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
pam_diagnose(ok, buf);
|
||||
pam_end(handle, ok);
|
||||
return;
|
||||
}
|
||||
|
||||
ok = pam_do(pam_authenticate, handle, 0, buf);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ok = pam_do(pam_acct_mgmt, handle, 0, buf);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ok = pam_do(pam_setcred, handle, PAM_ESTABLISH_CRED, buf);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ok = pam_do(pam_open_session, handle, 0, buf);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// clear the credentials
|
||||
input_text_clear(password);
|
||||
|
||||
// get passwd structure
|
||||
struct passwd* pwd = getpwnam(login->text);
|
||||
endpwent();
|
||||
|
||||
if (pwd == NULL)
|
||||
{
|
||||
dgn_throw(DGN_PWNAM);
|
||||
pam_end(handle, ok);
|
||||
return;
|
||||
}
|
||||
|
||||
// set user shell
|
||||
if (pwd->pw_shell[0] == '\0')
|
||||
{
|
||||
setusershell();
|
||||
|
||||
char* shell = getusershell();
|
||||
|
||||
if (shell != NULL)
|
||||
{
|
||||
strcpy(pwd->pw_shell, shell);
|
||||
}
|
||||
|
||||
endusershell();
|
||||
}
|
||||
|
||||
// restore regular terminal mode
|
||||
tb_clear();
|
||||
tb_present();
|
||||
tb_shutdown();
|
||||
|
||||
// start desktop environment
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
// set user info
|
||||
ok = initgroups(pwd->pw_name, pwd->pw_gid);
|
||||
|
||||
if (ok != 0)
|
||||
{
|
||||
dgn_throw(DGN_USER_INIT);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ok = setgid(pwd->pw_gid);
|
||||
|
||||
if (ok != 0)
|
||||
{
|
||||
dgn_throw(DGN_USER_GID);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ok = setuid(pwd->pw_uid);
|
||||
|
||||
if (ok != 0)
|
||||
{
|
||||
dgn_throw(DGN_USER_UID);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// get a display
|
||||
char tty_id [3];
|
||||
char vt[5];
|
||||
|
||||
snprintf(tty_id, 3, "%d", config.tty);
|
||||
snprintf(vt, 5, "vt%d", config.tty);
|
||||
|
||||
// set env
|
||||
env_init(pwd);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// add pam variables
|
||||
char** env = pam_getenvlist(handle);
|
||||
|
||||
for (u16 i = 0; env && env[i]; ++i)
|
||||
{
|
||||
putenv(env[i]);
|
||||
}
|
||||
|
||||
// add xdg variables
|
||||
env_xdg(tty_id, desktop->display_server[desktop->cur]);
|
||||
|
||||
// execute
|
||||
int ok = chdir(pwd->pw_dir);
|
||||
|
||||
if (ok != 0)
|
||||
{
|
||||
dgn_throw(DGN_CHDIR);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
reset_terminal(pwd);
|
||||
switch (desktop->display_server[desktop->cur])
|
||||
{
|
||||
case DS_WAYLAND:
|
||||
{
|
||||
wayland(pwd, desktop->cmd[desktop->cur]);
|
||||
break;
|
||||
}
|
||||
case DS_SHELL:
|
||||
{
|
||||
shell(pwd);
|
||||
break;
|
||||
}
|
||||
case DS_XINITRC:
|
||||
case DS_XORG:
|
||||
{
|
||||
xorg(pwd, vt, desktop->cmd[desktop->cur]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// add utmp audit
|
||||
struct utmp entry;
|
||||
add_utmp_entry(&entry, pwd->pw_name, pid);
|
||||
|
||||
// wait for the session to stop
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
remove_utmp_entry(&entry);
|
||||
|
||||
reset_terminal(pwd);
|
||||
|
||||
// reinit termbox
|
||||
tb_init();
|
||||
tb_select_output_mode(TB_OUTPUT_NORMAL);
|
||||
|
||||
// reload the desktop environment list on logout
|
||||
input_desktop_free(desktop);
|
||||
input_desktop(desktop);
|
||||
desktop_load(desktop);
|
||||
|
||||
// close pam session
|
||||
ok = pam_do(pam_close_session, handle, 0, buf);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ok = pam_do(pam_setcred, handle, PAM_DELETE_CRED, buf);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ok = pam_end(handle, 0);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
pam_diagnose(ok, buf);
|
||||
}
|
||||
}
|
||||
|
||||
13
src/login.h
Normal file
13
src/login.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef H_LY_LOGIN
|
||||
#define H_LY_LOGIN
|
||||
|
||||
#include "draw.h"
|
||||
#include "inputs.h"
|
||||
|
||||
void auth(
|
||||
struct desktop* desktop,
|
||||
struct text* login,
|
||||
struct text* password,
|
||||
struct term_buf* buf);
|
||||
|
||||
#endif
|
||||
315
src/main.c
Normal file
315
src/main.c
Normal file
@@ -0,0 +1,315 @@
|
||||
#include "argoat.h"
|
||||
#include "configator.h"
|
||||
#include "dragonfail.h"
|
||||
#include "termbox.h"
|
||||
#include "ctypes.h"
|
||||
|
||||
#include "draw.h"
|
||||
#include "inputs.h"
|
||||
#include "login.h"
|
||||
#include "utils.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ARG_COUNT 7
|
||||
// things you can define:
|
||||
// GIT_VERSION_STRING
|
||||
|
||||
// global
|
||||
struct lang lang;
|
||||
struct config config;
|
||||
|
||||
// args handles
|
||||
void arg_help(void* data, char** pars, const int pars_count)
|
||||
{
|
||||
printf("RTFM\n");
|
||||
}
|
||||
|
||||
void arg_version(void* data, char** pars, const int pars_count)
|
||||
{
|
||||
#ifdef GIT_VERSION_STRING
|
||||
printf("Ly version %s\n", GIT_VERSION_STRING);
|
||||
#else
|
||||
printf("Ly version unknown\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
// low-level error messages
|
||||
void log_init(char** log)
|
||||
{
|
||||
log[DGN_OK] = lang.err_dgn_oob;
|
||||
log[DGN_NULL] = lang.err_null;
|
||||
log[DGN_ALLOC] = lang.err_alloc;
|
||||
log[DGN_BOUNDS] = lang.err_bounds;
|
||||
log[DGN_DOMAIN] = lang.err_domain;
|
||||
log[DGN_MLOCK] = lang.err_mlock;
|
||||
log[DGN_XSESSIONS_DIR] = lang.err_xsessions_dir;
|
||||
log[DGN_XSESSIONS_OPEN] = lang.err_xsessions_open;
|
||||
log[DGN_PATH] = lang.err_path;
|
||||
log[DGN_CHDIR] = lang.err_chdir;
|
||||
log[DGN_PWNAM] = lang.err_pwnam;
|
||||
log[DGN_USER_INIT] = lang.err_user_init;
|
||||
log[DGN_USER_GID] = lang.err_user_gid;
|
||||
log[DGN_USER_UID] = lang.err_user_uid;
|
||||
log[DGN_PAM] = lang.err_pam;
|
||||
log[DGN_HOSTNAME] = lang.err_hostname;
|
||||
}
|
||||
|
||||
void arg_config(void* data, char** pars, const int pars_count)
|
||||
{
|
||||
*((char **)data) = *pars;
|
||||
}
|
||||
|
||||
// ly!
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// init error lib
|
||||
log_init(dgn_init());
|
||||
|
||||
// load config
|
||||
config_defaults();
|
||||
lang_defaults();
|
||||
|
||||
char *config_path = NULL;
|
||||
// parse args
|
||||
const struct argoat_sprig sprigs[ARG_COUNT] =
|
||||
{
|
||||
{NULL, 0, NULL, NULL},
|
||||
{"config", 0, &config_path, arg_config},
|
||||
{"c", 0, &config_path, arg_config},
|
||||
{"help", 0, NULL, arg_help},
|
||||
{"h", 0, NULL, arg_help},
|
||||
{"version", 0, NULL, arg_version},
|
||||
{"v", 0, NULL, arg_version},
|
||||
};
|
||||
|
||||
struct argoat args = {sprigs, ARG_COUNT, NULL, 0, 0};
|
||||
argoat_graze(&args, argc, argv);
|
||||
|
||||
// init inputs
|
||||
struct desktop desktop;
|
||||
struct text login;
|
||||
struct text password;
|
||||
input_desktop(&desktop);
|
||||
input_text(&login, config.max_login_len);
|
||||
input_text(&password, config.max_password_len);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
config_free();
|
||||
lang_free();
|
||||
return 1;
|
||||
}
|
||||
|
||||
config_load(config_path);
|
||||
|
||||
if (strcmp(config.lang, "en") != 0)
|
||||
{
|
||||
lang_load();
|
||||
}
|
||||
|
||||
void* input_structs[3] =
|
||||
{
|
||||
(void*) &desktop,
|
||||
(void*) &login,
|
||||
(void*) &password,
|
||||
};
|
||||
|
||||
void (*input_handles[3]) (void*, struct tb_event*) =
|
||||
{
|
||||
handle_desktop,
|
||||
handle_text,
|
||||
handle_text,
|
||||
};
|
||||
|
||||
desktop_load(&desktop);
|
||||
load(&desktop, &login);
|
||||
|
||||
// start termbox
|
||||
tb_init();
|
||||
tb_select_output_mode(TB_OUTPUT_NORMAL);
|
||||
tb_clear();
|
||||
|
||||
// init visible elements
|
||||
struct tb_event event;
|
||||
struct term_buf buf;
|
||||
u8 active_input = config.default_input;
|
||||
|
||||
(*input_handles[active_input])(input_structs[active_input], NULL);
|
||||
|
||||
// init drawing stuff
|
||||
draw_init(&buf);
|
||||
|
||||
if (config.animate)
|
||||
{
|
||||
animate_init(&buf);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
config.animate = false;
|
||||
dgn_reset();
|
||||
}
|
||||
}
|
||||
|
||||
// init state info
|
||||
int error;
|
||||
bool run = true;
|
||||
bool update = true;
|
||||
bool reboot = false;
|
||||
bool shutdown = false;
|
||||
u8 auth_fails = 0;
|
||||
|
||||
switch_tty(&buf);
|
||||
|
||||
// main loop
|
||||
while (run)
|
||||
{
|
||||
if (update)
|
||||
{
|
||||
if (auth_fails < 10)
|
||||
{
|
||||
tb_clear();
|
||||
animate(&buf);
|
||||
draw_box(&buf);
|
||||
draw_labels(&buf);
|
||||
draw_f_commands();
|
||||
draw_lock_state(&buf);
|
||||
position_input(&buf, &desktop, &login, &password);
|
||||
draw_desktop(&desktop);
|
||||
draw_input(&login);
|
||||
draw_input_mask(&password);
|
||||
update = config.animate;
|
||||
}
|
||||
else
|
||||
{
|
||||
usleep(10000);
|
||||
update = cascade(&buf, &auth_fails);
|
||||
}
|
||||
|
||||
tb_present();
|
||||
}
|
||||
|
||||
error = tb_peek_event(&event, config.min_refresh_delta);
|
||||
|
||||
if (error < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (event.type == TB_EVENT_KEY)
|
||||
{
|
||||
switch (event.key)
|
||||
{
|
||||
case TB_KEY_F1:
|
||||
shutdown = true;
|
||||
run = false;
|
||||
break;
|
||||
case TB_KEY_F2:
|
||||
reboot = true;
|
||||
run = false;
|
||||
break;
|
||||
case TB_KEY_CTRL_C:
|
||||
run = false;
|
||||
break;
|
||||
case TB_KEY_CTRL_U:
|
||||
if (active_input > 0)
|
||||
{
|
||||
input_text_clear(input_structs[active_input]);
|
||||
}
|
||||
break;
|
||||
case TB_KEY_ARROW_UP:
|
||||
if (active_input > 0)
|
||||
{
|
||||
--active_input;
|
||||
update = true;
|
||||
}
|
||||
break;
|
||||
case TB_KEY_ARROW_DOWN:
|
||||
if (active_input < 2)
|
||||
{
|
||||
++active_input;
|
||||
update = true;
|
||||
}
|
||||
break;
|
||||
case TB_KEY_TAB:
|
||||
++active_input;
|
||||
|
||||
if (active_input > 2)
|
||||
{
|
||||
active_input = PASSWORD_INPUT;
|
||||
}
|
||||
update = true;
|
||||
break;
|
||||
case TB_KEY_ENTER:
|
||||
save(&desktop, &login);
|
||||
auth(&desktop, &login, &password, &buf);
|
||||
update = true;
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
++auth_fails;
|
||||
// move focus back to password input
|
||||
active_input = PASSWORD_INPUT;
|
||||
|
||||
if (dgn_output_code() != DGN_PAM)
|
||||
{
|
||||
buf.info_line = dgn_output_log();
|
||||
}
|
||||
|
||||
if (config.blank_password)
|
||||
{
|
||||
input_text_clear(&password);
|
||||
}
|
||||
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.info_line = lang.logout;
|
||||
}
|
||||
|
||||
load(&desktop, &login);
|
||||
system("tput cnorm");
|
||||
break;
|
||||
default:
|
||||
(*input_handles[active_input])(
|
||||
input_structs[active_input],
|
||||
&event);
|
||||
update = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stop termbox
|
||||
tb_shutdown();
|
||||
|
||||
// free inputs
|
||||
input_desktop_free(&desktop);
|
||||
input_text_free(&login);
|
||||
input_text_free(&password);
|
||||
free_hostname();
|
||||
|
||||
// unload config
|
||||
draw_free(&buf);
|
||||
lang_free();
|
||||
|
||||
if (shutdown)
|
||||
{
|
||||
execl("/bin/sh", "sh", "-c", config.shutdown_cmd, NULL);
|
||||
}
|
||||
|
||||
if (reboot)
|
||||
{
|
||||
execl("/bin/sh", "sh", "-c", config.restart_cmd, NULL);
|
||||
}
|
||||
|
||||
config_free();
|
||||
|
||||
return 0;
|
||||
}
|
||||
974
src/main.zig
974
src/main.zig
@@ -1,974 +0,0 @@
|
||||
const std = @import("std");
|
||||
const build_options = @import("build_options");
|
||||
const builtin = @import("builtin");
|
||||
const clap = @import("clap");
|
||||
const ini = @import("zigini");
|
||||
const auth = @import("auth.zig");
|
||||
const bigclock = @import("bigclock.zig");
|
||||
const enums = @import("enums.zig");
|
||||
const Environment = @import("Environment.zig");
|
||||
const interop = @import("interop.zig");
|
||||
const ColorMix = @import("animations/ColorMix.zig");
|
||||
const Doom = @import("animations/Doom.zig");
|
||||
const Dummy = @import("animations/Dummy.zig");
|
||||
const Matrix = @import("animations/Matrix.zig");
|
||||
const Animation = @import("tui/Animation.zig");
|
||||
const TerminalBuffer = @import("tui/TerminalBuffer.zig");
|
||||
const Session = @import("tui/components/Session.zig");
|
||||
const Text = @import("tui/components/Text.zig");
|
||||
const InfoLine = @import("tui/components/InfoLine.zig");
|
||||
const Config = @import("config/Config.zig");
|
||||
const Lang = @import("config/Lang.zig");
|
||||
const Save = @import("config/Save.zig");
|
||||
const migrator = @import("config/migrator.zig");
|
||||
const SharedError = @import("SharedError.zig");
|
||||
|
||||
const Ini = ini.Ini;
|
||||
const DisplayServer = enums.DisplayServer;
|
||||
const Entry = Environment.Entry;
|
||||
const termbox = interop.termbox;
|
||||
const unistd = interop.unistd;
|
||||
const temporary_allocator = std.heap.page_allocator;
|
||||
const ly_top_str = "Ly version " ++ build_options.version;
|
||||
|
||||
var session_pid: std.posix.pid_t = -1;
|
||||
fn signalHandler(i: c_int) callconv(.C) void {
|
||||
if (session_pid == 0) return;
|
||||
|
||||
// Forward signal to session to clean up
|
||||
if (session_pid > 0) {
|
||||
_ = std.c.kill(session_pid, i);
|
||||
var status: c_int = 0;
|
||||
_ = std.c.waitpid(session_pid, &status, 0);
|
||||
}
|
||||
|
||||
_ = termbox.tb_shutdown();
|
||||
std.c.exit(i);
|
||||
}
|
||||
|
||||
fn ttyControlTransferSignalHandler(_: c_int) callconv(.C) void {
|
||||
_ = termbox.tb_shutdown();
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var shutdown = false;
|
||||
var restart = false;
|
||||
var shutdown_cmd: []const u8 = undefined;
|
||||
var restart_cmd: []const u8 = undefined;
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
|
||||
defer {
|
||||
// If we can't shutdown or restart due to an error, we print it to standard error. If that fails, just bail out
|
||||
if (shutdown) {
|
||||
const shutdown_error = std.process.execv(temporary_allocator, &[_][]const u8{ "/bin/sh", "-c", shutdown_cmd });
|
||||
stderr.print("error: couldn't shutdown: {any}\n", .{shutdown_error}) catch std.process.exit(1);
|
||||
} else if (restart) {
|
||||
const restart_error = std.process.execv(temporary_allocator, &[_][]const u8{ "/bin/sh", "-c", restart_cmd });
|
||||
stderr.print("error: couldn't restart: {any}\n", .{restart_error}) catch std.process.exit(1);
|
||||
} else {
|
||||
// The user has quit Ly using Ctrl+C
|
||||
temporary_allocator.free(shutdown_cmd);
|
||||
temporary_allocator.free(restart_cmd);
|
||||
}
|
||||
}
|
||||
|
||||
var gpa = std.heap.DebugAllocator(.{}).init;
|
||||
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();
|
||||
|
||||
// Load arguments
|
||||
const params = comptime clap.parseParamsComptime(
|
||||
\\-h, --help Shows all commands.
|
||||
\\-v, --version Shows the version of Ly.
|
||||
\\-c, --config <str> Overrides the default configuration path. Example: --config /usr/share/ly
|
||||
);
|
||||
|
||||
var diag = clap.Diagnostic{};
|
||||
var res = clap.parse(clap.Help, ¶ms, clap.parsers.default, .{ .diagnostic = &diag, .allocator = allocator }) catch |err| {
|
||||
diag.report(stderr, err) catch {};
|
||||
return err;
|
||||
};
|
||||
defer res.deinit();
|
||||
|
||||
var config: Config = undefined;
|
||||
var lang: Lang = undefined;
|
||||
var save: Save = undefined;
|
||||
var config_load_failed = false;
|
||||
var can_draw_clock = true;
|
||||
|
||||
if (res.args.help != 0) {
|
||||
try clap.help(stderr, clap.Help, ¶ms, .{});
|
||||
|
||||
_ = 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");
|
||||
std.process.exit(0);
|
||||
}
|
||||
if (res.args.version != 0) {
|
||||
_ = try stderr.write("Ly version " ++ build_options.version ++ "\n");
|
||||
std.process.exit(0);
|
||||
}
|
||||
|
||||
// Load configuration file
|
||||
var config_ini = Ini(Config).init(allocator);
|
||||
defer config_ini.deinit();
|
||||
|
||||
var lang_ini = Ini(Lang).init(allocator);
|
||||
defer lang_ini.deinit();
|
||||
|
||||
var save_ini = Ini(Save).init(allocator);
|
||||
defer save_ini.deinit();
|
||||
|
||||
var save_path: []const u8 = build_options.config_directory ++ "/ly/save.ini";
|
||||
var save_path_alloc = false;
|
||||
defer {
|
||||
if (save_path_alloc) allocator.free(save_path);
|
||||
}
|
||||
|
||||
const comment_characters = "#";
|
||||
|
||||
if (res.args.config) |s| {
|
||||
const trailing_slash = if (s[s.len - 1] != '/') "/" else "";
|
||||
|
||||
const config_path = try std.fmt.allocPrint(allocator, "{s}{s}config.ini", .{ s, trailing_slash });
|
||||
defer allocator.free(config_path);
|
||||
|
||||
config = config_ini.readFileToStruct(config_path, .{
|
||||
.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 });
|
||||
defer allocator.free(lang_path);
|
||||
|
||||
lang = lang_ini.readFileToStruct(lang_path, .{
|
||||
.fieldHandler = null,
|
||||
.comment_characters = comment_characters,
|
||||
}) catch Lang{};
|
||||
|
||||
if (config.load) {
|
||||
save_path = try std.fmt.allocPrint(allocator, "{s}{s}save.ini", .{ s, trailing_slash });
|
||||
save_path_alloc = true;
|
||||
|
||||
var user_buf: [32]u8 = undefined;
|
||||
save = save_ini.readFileToStruct(save_path, .{
|
||||
.fieldHandler = null,
|
||||
.comment_characters = comment_characters,
|
||||
}) catch migrator.tryMigrateSaveFile(&user_buf);
|
||||
}
|
||||
|
||||
migrator.lateConfigFieldHandler(&config.animation);
|
||||
} else {
|
||||
const config_path = build_options.config_directory ++ "/ly/config.ini";
|
||||
|
||||
config = config_ini.readFileToStruct(config_path, .{
|
||||
.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 });
|
||||
defer allocator.free(lang_path);
|
||||
|
||||
lang = lang_ini.readFileToStruct(lang_path, .{
|
||||
.fieldHandler = null,
|
||||
.comment_characters = comment_characters,
|
||||
}) catch Lang{};
|
||||
|
||||
if (config.load) {
|
||||
var user_buf: [32]u8 = undefined;
|
||||
save = save_ini.readFileToStruct(save_path, .{
|
||||
.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
|
||||
// we end up shutting down or restarting the system
|
||||
shutdown_cmd = try temporary_allocator.dupe(u8, config.shutdown_cmd);
|
||||
restart_cmd = try temporary_allocator.dupe(u8, config.restart_cmd);
|
||||
|
||||
// Initialize termbox
|
||||
_ = termbox.tb_init();
|
||||
defer _ = termbox.tb_shutdown();
|
||||
|
||||
const act = std.posix.Sigaction{
|
||||
.handler = .{ .handler = &signalHandler },
|
||||
.mask = std.posix.empty_sigset,
|
||||
.flags = 0,
|
||||
};
|
||||
std.posix.sigaction(std.posix.SIG.TERM, &act, null);
|
||||
|
||||
_ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_TRUECOLOR);
|
||||
_ = termbox.tb_clear();
|
||||
|
||||
// Needed to reset termbox after auth
|
||||
const tb_termios = try std.posix.tcgetattr(std.posix.STDIN_FILENO);
|
||||
|
||||
// Initialize terminal buffer
|
||||
const labels_max_length = @max(lang.login.len, lang.password.len);
|
||||
|
||||
var seed: u64 = undefined;
|
||||
std.crypto.random.bytes(std.mem.asBytes(&seed)); // Get a random seed for the PRNG (used by animations)
|
||||
|
||||
var prng = std.Random.DefaultPrng.init(seed);
|
||||
const random = prng.random();
|
||||
|
||||
const buffer_options = TerminalBuffer.InitOptions{
|
||||
.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
|
||||
var info_line = InfoLine.init(allocator, &buffer);
|
||||
defer info_line.deinit();
|
||||
|
||||
if (config_load_failed) {
|
||||
// We can't localize this since the config failed to load so we'd fallback to the default language anyway
|
||||
try info_line.addMessage("unable to parse config file", config.error_bg, config.error_fg);
|
||||
}
|
||||
|
||||
interop.setNumlock(config.numlock) catch {
|
||||
try info_line.addMessage(lang.err_numlock, config.error_bg, config.error_fg);
|
||||
};
|
||||
|
||||
var session = Session.init(allocator, &buffer);
|
||||
defer session.deinit();
|
||||
|
||||
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, ':');
|
||||
while (wayland_session_dirs.next()) |dir| {
|
||||
try crawl(&session, lang, dir, .wayland);
|
||||
}
|
||||
if (build_options.enable_x11_support) {
|
||||
var x_session_dirs = std.mem.splitScalar(u8, config.xsessions, ':');
|
||||
while (x_session_dirs.next()) |dir| {
|
||||
try crawl(&session, lang, dir, .x11);
|
||||
}
|
||||
}
|
||||
|
||||
var login = Text.init(allocator, &buffer, false, null);
|
||||
defer login.deinit();
|
||||
|
||||
var password = Text.init(allocator, &buffer, true, config.asterisk);
|
||||
defer password.deinit();
|
||||
|
||||
var active_input = config.default_input;
|
||||
var insert_mode = !config.vi_mode or config.vi_default_mode == .insert;
|
||||
|
||||
// Load last saved username and desktop selection, if any
|
||||
if (config.load) {
|
||||
if (save.user) |user| {
|
||||
try login.text.appendSlice(login.allocator, user);
|
||||
login.end = user.len;
|
||||
login.cursor = login.end;
|
||||
active_input = .password;
|
||||
}
|
||||
|
||||
if (save.session_index) |session_index| {
|
||||
if (session_index < session.label.list.items.len) session.label.current = session_index;
|
||||
}
|
||||
}
|
||||
|
||||
// Place components on the screen
|
||||
{
|
||||
buffer.drawBoxCenter(!config.hide_borders, config.blank_box);
|
||||
|
||||
const coordinates = buffer.calculateComponentCoordinates();
|
||||
info_line.label.position(coordinates.start_x, coordinates.y, coordinates.full_visible_length, null);
|
||||
session.label.position(coordinates.x, coordinates.y + 2, coordinates.visible_length, config.text_in_center);
|
||||
login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length);
|
||||
password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length);
|
||||
|
||||
switch (active_input) {
|
||||
.info_line => info_line.label.handle(null, insert_mode),
|
||||
.session => session.label.handle(null, insert_mode),
|
||||
.login => login.handle(null, insert_mode) catch {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
},
|
||||
.password => password.handle(null, insert_mode) catch {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the animation, if any
|
||||
var animation: Animation = undefined;
|
||||
|
||||
switch (config.animation) {
|
||||
.none => {
|
||||
var dummy = Dummy{};
|
||||
animation = dummy.animation();
|
||||
},
|
||||
.doom => {
|
||||
var doom = try Doom.init(allocator, &buffer, config.doom_top_color, config.doom_middle_color, config.doom_bottom_color);
|
||||
animation = doom.animation();
|
||||
},
|
||||
.matrix => {
|
||||
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 shutdown_key = try std.fmt.parseInt(u8, config.shutdown_key[1..], 10);
|
||||
const shutdown_len = try TerminalBuffer.strWidth(lang.shutdown);
|
||||
const restart_key = try std.fmt.parseInt(u8, config.restart_key[1..], 10);
|
||||
const restart_len = try TerminalBuffer.strWidth(lang.restart);
|
||||
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 run = true;
|
||||
var update = true;
|
||||
var resolution_changed = false;
|
||||
var auth_fails: u64 = 0;
|
||||
var can_access_console_dev = true;
|
||||
|
||||
// Switch to selected TTY if possible
|
||||
interop.switchTty(config.console_dev, config.tty) catch {
|
||||
try info_line.addMessage(lang.err_console_dev, config.error_bg, config.error_fg);
|
||||
can_access_console_dev = false;
|
||||
};
|
||||
|
||||
while (run) {
|
||||
// If there's no input or there's an animation, a resolution change needs to be checked
|
||||
if (!update or animate) {
|
||||
if (!update) std.Thread.sleep(std.time.ns_per_ms * 100);
|
||||
|
||||
_ = termbox.tb_present(); // Required to update tb_width() and tb_height()
|
||||
|
||||
const width: usize = @intCast(termbox.tb_width());
|
||||
const height: usize = @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
|
||||
|
||||
buffer.width = width;
|
||||
buffer.height = height;
|
||||
|
||||
animation.realloc() catch {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
};
|
||||
|
||||
update = true;
|
||||
resolution_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (update) {
|
||||
// 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) {
|
||||
_ = termbox.tb_clear();
|
||||
|
||||
if (!animation_timed_out) animation.draw();
|
||||
|
||||
buffer.drawLabel(ly_top_str, 0, 0);
|
||||
|
||||
if (config.bigclock != .none and buffer.box_height + (bigclock.HEIGHT + 2) * 2 < buffer.height) {
|
||||
const format = "%H:%M";
|
||||
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;
|
||||
|
||||
var clock_buf: [format.len + 1:0]u8 = undefined;
|
||||
const clock_str = interop.timeAsString(&clock_buf, format);
|
||||
|
||||
for (clock_str, 0..) |c, i| {
|
||||
const clock_cell = bigclock.clockCell(animate, c, buffer.fg, buffer.bg, config.bigclock);
|
||||
bigclock.alphaBlit(xo + i * (bigclock.WIDTH + 1), yo, buffer.width, buffer.height, clock_cell);
|
||||
}
|
||||
}
|
||||
|
||||
buffer.drawBoxCenter(!config.hide_borders, config.blank_box);
|
||||
|
||||
if (resolution_changed) {
|
||||
const coordinates = buffer.calculateComponentCoordinates();
|
||||
info_line.label.position(coordinates.start_x, coordinates.y, coordinates.full_visible_length, null);
|
||||
session.label.position(coordinates.x, coordinates.y + 2, coordinates.visible_length, config.text_in_center);
|
||||
login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length);
|
||||
password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length);
|
||||
|
||||
resolution_changed = false;
|
||||
}
|
||||
|
||||
switch (active_input) {
|
||||
.info_line => info_line.label.handle(null, insert_mode),
|
||||
.session => session.label.handle(null, insert_mode),
|
||||
.login => login.handle(null, insert_mode) catch {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
},
|
||||
.password => password.handle(null, insert_mode) catch {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
},
|
||||
}
|
||||
|
||||
if (config.clock) |clock| draw_clock: {
|
||||
if (!can_draw_clock) break :draw_clock;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
buffer.drawLabel(clock_str, buffer.width - @min(buffer.width, clock_str.len), 0);
|
||||
}
|
||||
|
||||
const label_x = buffer.box_x + buffer.margin_box_h;
|
||||
const label_y = buffer.box_y + buffer.margin_box_v;
|
||||
|
||||
buffer.drawLabel(lang.login, label_x, label_y + 4);
|
||||
buffer.drawLabel(lang.password, label_x, label_y + 6);
|
||||
|
||||
info_line.label.draw();
|
||||
|
||||
if (!config.hide_key_hints) {
|
||||
var length: usize = ly_top_str.len + 1;
|
||||
|
||||
buffer.drawLabel(config.shutdown_key, length, 0);
|
||||
length += config.shutdown_key.len + 1;
|
||||
buffer.drawLabel(" ", length - 1, 0);
|
||||
|
||||
buffer.drawLabel(lang.shutdown, length, 0);
|
||||
length += shutdown_len + 1;
|
||||
|
||||
buffer.drawLabel(config.restart_key, length, 0);
|
||||
length += config.restart_key.len + 1;
|
||||
buffer.drawLabel(" ", length - 1, 0);
|
||||
|
||||
buffer.drawLabel(lang.restart, length, 0);
|
||||
length += restart_len + 1;
|
||||
|
||||
if (config.sleep_cmd != null) {
|
||||
buffer.drawLabel(config.sleep_key, length, 0);
|
||||
length += config.sleep_key.len + 1;
|
||||
buffer.drawLabel(" ", length - 1, 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;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.box_title) |title| {
|
||||
buffer.drawConfinedLabel(title, buffer.box_x, buffer.box_y - 1, buffer.box_width);
|
||||
}
|
||||
|
||||
if (config.vi_mode) {
|
||||
const label_txt = if (insert_mode) lang.insert else lang.normal;
|
||||
buffer.drawLabel(label_txt, buffer.box_x, buffer.box_y + buffer.box_height);
|
||||
}
|
||||
|
||||
if (can_access_console_dev) draw_lock_state: {
|
||||
const lock_state = interop.getLockState(config.console_dev) catch {
|
||||
try info_line.addMessage(lang.err_console_dev, config.error_bg, config.error_fg);
|
||||
break :draw_lock_state;
|
||||
};
|
||||
|
||||
var lock_state_x = buffer.width - @min(buffer.width, lang.numlock.len);
|
||||
const lock_state_y: usize = if (config.clock != null) 1 else 0;
|
||||
|
||||
if (lock_state.numlock) buffer.drawLabel(lang.numlock, lock_state_x, lock_state_y);
|
||||
|
||||
if (lock_state_x >= lang.capslock.len + 1) {
|
||||
lock_state_x -= lang.capslock.len + 1;
|
||||
if (lock_state.capslock) buffer.drawLabel(lang.capslock, lock_state_x, lock_state_y);
|
||||
}
|
||||
}
|
||||
|
||||
session.label.draw();
|
||||
login.draw();
|
||||
password.draw();
|
||||
} else {
|
||||
std.Thread.sleep(std.time.ns_per_ms * 10);
|
||||
update = buffer.cascade();
|
||||
|
||||
if (!update) {
|
||||
std.Thread.sleep(std.time.ns_per_s * 7);
|
||||
auth_fails = 0;
|
||||
}
|
||||
}
|
||||
|
||||
_ = termbox.tb_present();
|
||||
}
|
||||
|
||||
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
|
||||
if (animate and !animation_timed_out) {
|
||||
timeout = config.min_refresh_delta;
|
||||
|
||||
// check how long we have been running so we can turn off the animation
|
||||
var tv: interop.system_time.timeval = undefined;
|
||||
_ = 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);
|
||||
} else if (config.clock != null or auth_fails >= config.auth_fails) {
|
||||
var tv: interop.system_time.timeval = undefined;
|
||||
_ = interop.system_time.gettimeofday(&tv, null);
|
||||
|
||||
timeout = @intCast(1000 - @divTrunc(tv.tv_usec, 1000) + 1);
|
||||
}
|
||||
|
||||
const event_error = if (timeout == -1) termbox.tb_poll_event(&event) else termbox.tb_peek_event(&event, timeout);
|
||||
|
||||
update = timeout != -1;
|
||||
|
||||
if (event_error < 0 or event.type != termbox.TB_EVENT_KEY) continue;
|
||||
|
||||
switch (event.key) {
|
||||
termbox.TB_KEY_ESC => {
|
||||
if (config.vi_mode and insert_mode) {
|
||||
insert_mode = false;
|
||||
update = true;
|
||||
}
|
||||
},
|
||||
termbox.TB_KEY_F12...termbox.TB_KEY_F1 => {
|
||||
const pressed_key = 0xFFFF - event.key + 1;
|
||||
if (pressed_key == shutdown_key) {
|
||||
shutdown = true;
|
||||
run = false;
|
||||
} else if (pressed_key == restart_key) {
|
||||
restart = true;
|
||||
run = false;
|
||||
} else if (pressed_key == sleep_key) {
|
||||
if (config.sleep_cmd) |sleep_cmd| {
|
||||
var sleep = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", sleep_cmd }, allocator);
|
||||
sleep.stdout_behavior = .Ignore;
|
||||
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_U => {
|
||||
if (active_input == .login) {
|
||||
login.clear();
|
||||
update = true;
|
||||
} else if (active_input == .password) {
|
||||
password.clear();
|
||||
update = true;
|
||||
}
|
||||
},
|
||||
termbox.TB_KEY_CTRL_K, termbox.TB_KEY_ARROW_UP => {
|
||||
active_input = switch (active_input) {
|
||||
.session, .info_line => .info_line,
|
||||
.login => .session,
|
||||
.password => .login,
|
||||
};
|
||||
update = true;
|
||||
},
|
||||
termbox.TB_KEY_CTRL_J, termbox.TB_KEY_ARROW_DOWN => {
|
||||
active_input = switch (active_input) {
|
||||
.info_line => .session,
|
||||
.session => .login,
|
||||
.login, .password => .password,
|
||||
};
|
||||
update = true;
|
||||
},
|
||||
termbox.TB_KEY_TAB => {
|
||||
active_input = switch (active_input) {
|
||||
.info_line => .session,
|
||||
.session => .login,
|
||||
.login => .password,
|
||||
.password => .info_line,
|
||||
};
|
||||
update = true;
|
||||
},
|
||||
termbox.TB_KEY_BACK_TAB => {
|
||||
active_input = switch (active_input) {
|
||||
.info_line => .password,
|
||||
.session => .info_line,
|
||||
.login => .session,
|
||||
.password => .login,
|
||||
};
|
||||
|
||||
update = true;
|
||||
},
|
||||
termbox.TB_KEY_ENTER => authenticate: {
|
||||
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: {
|
||||
var file = std.fs.cwd().createFile(save_path, .{}) catch break :save_last_settings;
|
||||
defer file.close();
|
||||
|
||||
const save_data = Save{
|
||||
.user = login.text.items,
|
||||
.session_index = session.label.current,
|
||||
};
|
||||
ini.writeFromStruct(save_data, file.writer(), null, .{}) 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();
|
||||
defer shared_err.deinit();
|
||||
|
||||
{
|
||||
const login_text = try allocator.dupeZ(u8, login.text.items);
|
||||
defer allocator.free(login_text);
|
||||
const password_text = try allocator.dupeZ(u8, password.text.items);
|
||||
defer allocator.free(password_text);
|
||||
|
||||
session_pid = try std.posix.fork();
|
||||
if (session_pid == 0) {
|
||||
const current_environment = session.label.list.items[session.label.current];
|
||||
const auth_options = auth.AuthOptions{
|
||||
.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);
|
||||
std.process.exit(1);
|
||||
};
|
||||
std.process.exit(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;
|
||||
}
|
||||
|
||||
// Take back control of the TTY
|
||||
_ = termbox.tb_init();
|
||||
_ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_TRUECOLOR);
|
||||
|
||||
const auth_err = shared_err.readError();
|
||||
if (auth_err) |err| {
|
||||
auth_fails += 1;
|
||||
active_input = .password;
|
||||
try info_line.addMessage(getAuthErrorMsg(err, lang), config.error_bg, config.error_fg);
|
||||
if (config.clear_password or err != error.PamAuthError) password.clear();
|
||||
} 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();
|
||||
try info_line.addMessage(lang.logout, config.bg, config.fg);
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (auth_fails < config.auth_fails) _ = termbox.tb_clear();
|
||||
|
||||
update = true;
|
||||
|
||||
// Restore the cursor
|
||||
_ = termbox.tb_set_cursor(0, 0);
|
||||
_ = termbox.tb_present();
|
||||
},
|
||||
else => {
|
||||
if (!insert_mode) {
|
||||
switch (event.ch) {
|
||||
'k' => {
|
||||
active_input = switch (active_input) {
|
||||
.session, .info_line => .info_line,
|
||||
.login => .session,
|
||||
.password => .login,
|
||||
};
|
||||
update = true;
|
||||
continue;
|
||||
},
|
||||
'j' => {
|
||||
active_input = switch (active_input) {
|
||||
.info_line => .session,
|
||||
.session => .login,
|
||||
.login, .password => .password,
|
||||
};
|
||||
update = true;
|
||||
continue;
|
||||
},
|
||||
'i' => {
|
||||
insert_mode = true;
|
||||
update = true;
|
||||
continue;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
switch (active_input) {
|
||||
.info_line => info_line.label.handle(&event, insert_mode),
|
||||
.session => session.label.handle(&event, insert_mode),
|
||||
.login => login.handle(&event, insert_mode) catch {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
},
|
||||
.password => password.handle(&event, insert_mode) catch {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
},
|
||||
}
|
||||
update = true;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
return switch (err) {
|
||||
error.GetPasswordNameFailed => lang.err_pwnam,
|
||||
error.GetEnvListFailed => lang.err_envlist,
|
||||
error.XauthFailed => lang.err_xauth,
|
||||
error.XcbConnectionFailed => lang.err_xcb_conn,
|
||||
error.GroupInitializationFailed => lang.err_user_init,
|
||||
error.SetUserGidFailed => lang.err_user_gid,
|
||||
error.SetUserUidFailed => lang.err_user_uid,
|
||||
error.ChangeDirectoryFailed => lang.err_perm_dir,
|
||||
error.TtyControlTransferFailed => lang.err_tty_ctrl,
|
||||
error.SetPathFailed => lang.err_path,
|
||||
error.PamAccountExpired => lang.err_pam_acct_expired,
|
||||
error.PamAuthError => lang.err_pam_auth,
|
||||
error.PamAuthInfoUnavailable => lang.err_pam_authinfo_unavail,
|
||||
error.PamBufferError => lang.err_pam_buf,
|
||||
error.PamCredentialsError => lang.err_pam_cred_err,
|
||||
error.PamCredentialsExpired => lang.err_pam_cred_expired,
|
||||
error.PamCredentialsInsufficient => lang.err_pam_cred_insufficient,
|
||||
error.PamCredentialsUnavailable => lang.err_pam_cred_unavail,
|
||||
error.PamMaximumTries => lang.err_pam_maxtries,
|
||||
error.PamNewAuthTokenRequired => lang.err_pam_authok_reqd,
|
||||
error.PamPermissionDenied => lang.err_pam_perm_denied,
|
||||
error.PamSessionError => lang.err_pam_session,
|
||||
error.PamSystemError => lang.err_pam_sys,
|
||||
error.PamUserUnknown => lang.err_pam_user_unknown,
|
||||
error.PamAbort => lang.err_pam_abort,
|
||||
else => lang.err_unknown,
|
||||
};
|
||||
}
|
||||
@@ -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});
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const interop = @import("../interop.zig");
|
||||
const Cell = @import("Cell.zig");
|
||||
|
||||
const Random = std.Random;
|
||||
|
||||
const termbox = interop.termbox;
|
||||
|
||||
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,
|
||||
width: usize,
|
||||
height: usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
border_fg: u32,
|
||||
box_chars: struct {
|
||||
left_up: u32,
|
||||
left_down: u32,
|
||||
right_up: u32,
|
||||
right_down: u32,
|
||||
top: u32,
|
||||
bottom: u32,
|
||||
left: u32,
|
||||
right: u32,
|
||||
},
|
||||
labels_max_length: usize,
|
||||
box_x: usize,
|
||||
box_y: usize,
|
||||
box_width: usize,
|
||||
box_height: usize,
|
||||
margin_box_v: u8,
|
||||
margin_box_h: u8,
|
||||
blank_cell: Cell,
|
||||
|
||||
pub fn init(options: InitOptions, labels_max_length: usize, random: Random) TerminalBuffer {
|
||||
return .{
|
||||
.random = random,
|
||||
.width = @intCast(termbox.tb_width()),
|
||||
.height = @intCast(termbox.tb_height()),
|
||||
.fg = options.fg,
|
||||
.bg = options.bg,
|
||||
.border_fg = options.border_fg,
|
||||
.box_chars = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) .{
|
||||
.left_up = 0x250C,
|
||||
.left_down = 0x2514,
|
||||
.right_up = 0x2510,
|
||||
.right_down = 0x2518,
|
||||
.top = 0x2500,
|
||||
.bottom = 0x2500,
|
||||
.left = 0x2502,
|
||||
.right = 0x2502,
|
||||
} else .{
|
||||
.left_up = '+',
|
||||
.left_down = '+',
|
||||
.right_up = '+',
|
||||
.right_down = '+',
|
||||
.top = '-',
|
||||
.bottom = '-',
|
||||
.left = '|',
|
||||
.right = '|',
|
||||
},
|
||||
.labels_max_length = labels_max_length,
|
||||
.box_x = 0,
|
||||
.box_y = 0,
|
||||
.box_width = (2 * options.margin_box_h) + options.input_len + 1 + labels_max_length,
|
||||
.box_height = 7 + (2 * options.margin_box_v),
|
||||
.margin_box_v = options.margin_box_v,
|
||||
.margin_box_h = options.margin_box_h,
|
||||
.blank_cell = Cell.init(' ', options.fg, options.bg),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn cascade(self: TerminalBuffer) bool {
|
||||
var changed = false;
|
||||
var y = self.height - 2;
|
||||
|
||||
while (y > 0) : (y -= 1) {
|
||||
for (0..self.width) |x| {
|
||||
var cell: termbox.tb_cell = undefined;
|
||||
var cell_under: termbox.tb_cell = undefined;
|
||||
|
||||
_ = termbox.tb_get_cell(@intCast(x), @intCast(y - 1), 1, &cell);
|
||||
_ = termbox.tb_get_cell(@intCast(x), @intCast(y), 1, &cell_under);
|
||||
|
||||
const char: u8 = @truncate(cell.ch);
|
||||
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;
|
||||
|
||||
_ = termbox.tb_set_cell(@intCast(x), @intCast(y), cell.ch, cell.fg, cell.bg);
|
||||
_ = termbox.tb_set_cell(@intCast(x), @intCast(y - 1), ' ', cell_under.fg, cell_under.bg);
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
pub fn drawBoxCenter(self: *TerminalBuffer, show_borders: bool, blank_box: bool) void {
|
||||
if (self.width < 2 or self.height < 2) return;
|
||||
const x1 = (self.width - @min(self.width - 2, self.box_width)) / 2;
|
||||
const y1 = (self.height - @min(self.height - 2, self.box_height)) / 2;
|
||||
const x2 = (self.width + @min(self.width, self.box_width)) / 2;
|
||||
const y2 = (self.height + @min(self.height, self.box_height)) / 2;
|
||||
|
||||
self.box_x = x1;
|
||||
self.box_y = y1;
|
||||
|
||||
if (show_borders) {
|
||||
_ = termbox.tb_set_cell(@intCast(x1 - 1), @intCast(y1 - 1), self.box_chars.left_up, self.border_fg, self.bg);
|
||||
_ = termbox.tb_set_cell(@intCast(x2), @intCast(y1 - 1), self.box_chars.right_up, 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);
|
||||
|
||||
var c1 = Cell.init(self.box_chars.top, self.border_fg, self.bg);
|
||||
var c2 = Cell.init(self.box_chars.bottom, self.border_fg, self.bg);
|
||||
|
||||
for (0..self.box_width) |i| {
|
||||
c1.put(x1 + i, y1 - 1);
|
||||
c2.put(x1 + i, y2);
|
||||
}
|
||||
|
||||
c1.ch = self.box_chars.left;
|
||||
c2.ch = self.box_chars.right;
|
||||
|
||||
for (0..self.box_height) |i| {
|
||||
c1.put(x1 - 1, y1 + i);
|
||||
c2.put(x2, y1 + i);
|
||||
}
|
||||
}
|
||||
|
||||
if (blank_box) {
|
||||
for (0..self.box_height) |y| {
|
||||
for (0..self.box_width) |x| {
|
||||
self.blank_cell.put(x1 + x, y1 + y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calculateComponentCoordinates(self: TerminalBuffer) struct {
|
||||
start_x: usize,
|
||||
x: usize,
|
||||
y: usize,
|
||||
full_visible_length: usize,
|
||||
visible_length: usize,
|
||||
} {
|
||||
const start_x = self.box_x + self.margin_box_h;
|
||||
const x = start_x + self.labels_max_length + 1;
|
||||
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;
|
||||
|
||||
return .{
|
||||
.start_x = start_x,
|
||||
.x = x,
|
||||
.y = y,
|
||||
.full_visible_length = full_visible_length,
|
||||
.visible_length = visible_length,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn drawLabel(self: TerminalBuffer, text: []const u8, x: usize, y: usize) 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 utf8view = std.unicode.Utf8View.init(text) catch return;
|
||||
var utf8 = utf8view.iterator();
|
||||
|
||||
var i: c_int = @intCast(x);
|
||||
while (utf8.nextCodepoint()) |codepoint| : (i += termbox.tb_wcwidth(codepoint)) {
|
||||
_ = termbox.tb_set_cell(i, yc, codepoint, fg, bg);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drawConfinedLabel(self: TerminalBuffer, text: []const u8, x: usize, y: usize, max_length: usize) void {
|
||||
const yc: c_int = @intCast(y);
|
||||
const utf8view = std.unicode.Utf8View.init(text) catch return;
|
||||
var utf8 = utf8view.iterator();
|
||||
|
||||
var i: c_int = @intCast(x);
|
||||
while (utf8.nextCodepoint()) |codepoint| : (i += termbox.tb_wcwidth(codepoint)) {
|
||||
if (i >= max_length) break;
|
||||
_ = termbox.tb_set_cell(i, yc, codepoint, self.fg, self.bg);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drawCharMultiple(self: TerminalBuffer, char: u32, x: usize, y: usize, length: usize) void {
|
||||
const cell = Cell.init(char, self.fg, self.bg);
|
||||
for (0..length) |xx| cell.put(x + xx, y);
|
||||
}
|
||||
|
||||
// Every codepoint is assumed to have a width of 1.
|
||||
// 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);
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
const std = @import("std");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const generic = @import("generic.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const MessageLabel = generic.CyclableLabel(Message);
|
||||
|
||||
const InfoLine = @This();
|
||||
|
||||
const Message = struct {
|
||||
width: u8,
|
||||
text: []const u8,
|
||||
bg: u32,
|
||||
fg: u32,
|
||||
};
|
||||
|
||||
label: MessageLabel,
|
||||
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer) InfoLine {
|
||||
return .{
|
||||
.label = MessageLabel.init(allocator, buffer, drawItem),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *InfoLine) void {
|
||||
self.label.deinit();
|
||||
}
|
||||
|
||||
pub fn addMessage(self: *InfoLine, text: []const u8, bg: u32, fg: u32) !void {
|
||||
if (text.len == 0) return;
|
||||
|
||||
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 spaces = try allocator.alloc(u8, buffer.box_width);
|
||||
defer allocator.free(spaces);
|
||||
|
||||
@memset(spaces, ' ');
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
const std = @import("std");
|
||||
const interop = @import("../../interop.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const DynamicString = std.ArrayListUnmanaged(u8);
|
||||
|
||||
const termbox = interop.termbox;
|
||||
|
||||
const Text = @This();
|
||||
|
||||
allocator: Allocator,
|
||||
buffer: *TerminalBuffer,
|
||||
text: DynamicString,
|
||||
end: usize,
|
||||
cursor: usize,
|
||||
visible_start: usize,
|
||||
visible_length: usize,
|
||||
x: usize,
|
||||
y: usize,
|
||||
masked: bool,
|
||||
maybe_mask: ?u32,
|
||||
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, masked: bool, maybe_mask: ?u32) Text {
|
||||
const text: DynamicString = .empty;
|
||||
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.buffer = buffer,
|
||||
.text = text,
|
||||
.end = 0,
|
||||
.cursor = 0,
|
||||
.visible_start = 0,
|
||||
.visible_length = 0,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.masked = masked,
|
||||
.maybe_mask = maybe_mask,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Text) void {
|
||||
self.text.deinit(self.allocator);
|
||||
}
|
||||
|
||||
pub fn position(self: *Text, x: usize, y: usize, visible_length: usize) void {
|
||||
self.x = x;
|
||||
self.y = y;
|
||||
self.visible_length = visible_length;
|
||||
}
|
||||
|
||||
pub fn handle(self: *Text, 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 => self.goLeft(),
|
||||
termbox.TB_KEY_ARROW_RIGHT => self.goRight(),
|
||||
termbox.TB_KEY_DELETE => self.delete(),
|
||||
termbox.TB_KEY_BACKSPACE, termbox.TB_KEY_BACKSPACE2 => {
|
||||
if (insert_mode) {
|
||||
self.backspace();
|
||||
} else {
|
||||
self.goLeft();
|
||||
}
|
||||
},
|
||||
termbox.TB_KEY_SPACE => try self.write(' '),
|
||||
else => {
|
||||
if (event.ch > 31 and event.ch < 127) {
|
||||
if (insert_mode) {
|
||||
try self.write(@intCast(event.ch));
|
||||
} else {
|
||||
switch (event.ch) {
|
||||
'h' => self.goLeft(),
|
||||
'l' => self.goRight(),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
if (length == 0) return;
|
||||
|
||||
const visible_slice = vs: {
|
||||
if (self.text.items.len > self.visible_length and self.cursor < self.text.items.len) {
|
||||
break :vs self.text.items[self.visible_start..(self.visible_length + self.visible_start)];
|
||||
} else {
|
||||
break :vs self.text.items[self.visible_start..];
|
||||
}
|
||||
};
|
||||
|
||||
self.buffer.drawLabel(visible_slice, self.x, self.y);
|
||||
}
|
||||
|
||||
pub fn clear(self: *Text) void {
|
||||
self.text.clearRetainingCapacity();
|
||||
self.end = 0;
|
||||
self.cursor = 0;
|
||||
self.visible_start = 0;
|
||||
}
|
||||
|
||||
fn goLeft(self: *Text) void {
|
||||
if (self.cursor == 0) return;
|
||||
if (self.visible_start > 0) self.visible_start -= 1;
|
||||
|
||||
self.cursor -= 1;
|
||||
}
|
||||
|
||||
fn goRight(self: *Text) void {
|
||||
if (self.cursor >= self.end) return;
|
||||
if (self.cursor - self.visible_start == self.visible_length - 1) self.visible_start += 1;
|
||||
|
||||
self.cursor += 1;
|
||||
}
|
||||
|
||||
fn delete(self: *Text) void {
|
||||
if (self.cursor >= self.end) return;
|
||||
|
||||
_ = self.text.orderedRemove(self.cursor);
|
||||
|
||||
self.end -= 1;
|
||||
}
|
||||
|
||||
fn backspace(self: *Text) void {
|
||||
if (self.cursor == 0) return;
|
||||
|
||||
self.goLeft();
|
||||
self.delete();
|
||||
}
|
||||
|
||||
fn write(self: *Text, char: u8) !void {
|
||||
if (char == 0) return;
|
||||
|
||||
try self.text.insert(self.allocator, self.cursor, char);
|
||||
|
||||
self.end += 1;
|
||||
self.goRight();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
276
src/utils.c
Normal file
276
src/utils.c
Normal file
@@ -0,0 +1,276 @@
|
||||
#include "configator.h"
|
||||
#include "dragonfail.h"
|
||||
|
||||
#include "inputs.h"
|
||||
#include "config.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
#include <sys/consio.h>
|
||||
#else // linux
|
||||
#include <linux/vt.h>
|
||||
#endif
|
||||
|
||||
void desktop_crawl(
|
||||
struct desktop* target,
|
||||
char* sessions,
|
||||
enum display_server server)
|
||||
{
|
||||
DIR* dir;
|
||||
struct dirent* dir_info;
|
||||
int ok;
|
||||
|
||||
ok = access(sessions, F_OK);
|
||||
|
||||
if (ok == -1)
|
||||
{
|
||||
dgn_throw(DGN_XSESSIONS_DIR);
|
||||
return;
|
||||
}
|
||||
|
||||
dir = opendir(sessions);
|
||||
|
||||
if (dir == NULL)
|
||||
{
|
||||
dgn_throw(DGN_XSESSIONS_OPEN);
|
||||
return;
|
||||
}
|
||||
|
||||
char* name = NULL;
|
||||
char* exec = NULL;
|
||||
|
||||
struct configator_param map_desktop[] =
|
||||
{
|
||||
{"Exec", &exec, config_handle_str},
|
||||
{"Name", &name, config_handle_str},
|
||||
};
|
||||
|
||||
struct configator_param* map[] =
|
||||
{
|
||||
NULL,
|
||||
map_desktop,
|
||||
};
|
||||
|
||||
struct configator_param sections[] =
|
||||
{
|
||||
{"Desktop Entry", NULL, NULL},
|
||||
};
|
||||
|
||||
uint16_t map_len[] = {0, 2};
|
||||
uint16_t sections_len = 1;
|
||||
|
||||
struct configator desktop_config;
|
||||
desktop_config.map = map;
|
||||
desktop_config.map_len = map_len;
|
||||
desktop_config.sections = sections;
|
||||
desktop_config.sections_len = sections_len;
|
||||
|
||||
#if defined(NAME_MAX)
|
||||
char path[NAME_MAX];
|
||||
#elif defined(_POSIX_PATH_MAX)
|
||||
char path[_POSIX_PATH_MAX];
|
||||
#else
|
||||
char path[1024];
|
||||
#endif
|
||||
|
||||
dir_info = readdir(dir);
|
||||
|
||||
while (dir_info != NULL)
|
||||
{
|
||||
if ((dir_info->d_name)[0] == '.')
|
||||
{
|
||||
dir_info = readdir(dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(path, (sizeof (path)) - 1, "%s/", sessions);
|
||||
strncat(path, dir_info->d_name, (sizeof (path)) - 1);
|
||||
configator(&desktop_config, path);
|
||||
|
||||
// if these are wayland sessions, add " (Wayland)" to their names,
|
||||
// as long as their names don't already contain that string
|
||||
if (server == DS_WAYLAND && config.wayland_specifier)
|
||||
{
|
||||
const char wayland_specifier[] = " (Wayland)";
|
||||
if (strstr(name, wayland_specifier) == NULL)
|
||||
{
|
||||
name = realloc(name, (strlen(name) + sizeof(wayland_specifier) + 1));
|
||||
// using strcat is safe because the string is constant
|
||||
strcat(name, wayland_specifier);
|
||||
}
|
||||
}
|
||||
|
||||
if ((name != NULL) && (exec != NULL))
|
||||
{
|
||||
input_desktop_add(target, name, exec, server);
|
||||
}
|
||||
|
||||
name = NULL;
|
||||
exec = NULL;
|
||||
dir_info = readdir(dir);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
void desktop_load(struct desktop* target)
|
||||
{
|
||||
// we don't care about desktop environments presence
|
||||
// because the fallback shell is always available
|
||||
// so we just dismiss any "throw" for now
|
||||
int err = 0;
|
||||
|
||||
desktop_crawl(target, config.waylandsessions, DS_WAYLAND);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
++err;
|
||||
dgn_reset();
|
||||
}
|
||||
|
||||
desktop_crawl(target, config.xsessions, DS_XORG);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
++err;
|
||||
dgn_reset();
|
||||
}
|
||||
}
|
||||
|
||||
static char* hostname_backup = NULL;
|
||||
|
||||
void hostname(char** out)
|
||||
{
|
||||
if (hostname_backup != NULL)
|
||||
{
|
||||
*out = hostname_backup;
|
||||
return;
|
||||
}
|
||||
|
||||
int maxlen = sysconf(_SC_HOST_NAME_MAX);
|
||||
|
||||
if (maxlen < 0)
|
||||
{
|
||||
maxlen = _POSIX_HOST_NAME_MAX;
|
||||
}
|
||||
|
||||
hostname_backup = malloc(maxlen + 1);
|
||||
|
||||
if (hostname_backup == NULL)
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gethostname(hostname_backup, maxlen) < 0)
|
||||
{
|
||||
dgn_throw(DGN_HOSTNAME);
|
||||
return;
|
||||
}
|
||||
|
||||
hostname_backup[maxlen] = '\0';
|
||||
*out = hostname_backup;
|
||||
}
|
||||
|
||||
void free_hostname()
|
||||
{
|
||||
free(hostname_backup);
|
||||
}
|
||||
|
||||
void switch_tty(struct term_buf* buf)
|
||||
{
|
||||
FILE* console = fopen(config.console_dev, "w");
|
||||
|
||||
if (console == NULL)
|
||||
{
|
||||
buf->info_line = lang.err_console_dev;
|
||||
return;
|
||||
}
|
||||
|
||||
int fd = fileno(console);
|
||||
|
||||
ioctl(fd, VT_ACTIVATE, config.tty);
|
||||
ioctl(fd, VT_WAITACTIVE, config.tty);
|
||||
|
||||
fclose(console);
|
||||
}
|
||||
|
||||
void save(struct desktop* desktop, struct text* login)
|
||||
{
|
||||
if (config.save)
|
||||
{
|
||||
FILE* fp = fopen(config.save_file, "wb+");
|
||||
|
||||
if (fp != NULL)
|
||||
{
|
||||
fprintf(fp, "%s\n%d", login->text, desktop->cur);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void load(struct desktop* desktop, struct text* login)
|
||||
{
|
||||
if (!config.load)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FILE* fp = fopen(config.save_file, "rb");
|
||||
|
||||
if (fp == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char* line = malloc(config.max_login_len + 1);
|
||||
|
||||
if (line == NULL)
|
||||
{
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fgets(line, config.max_login_len + 1, fp))
|
||||
{
|
||||
int len = strlen(line);
|
||||
strncpy(login->text, line, login->len);
|
||||
|
||||
if (len == 0)
|
||||
{
|
||||
login->end = login->text;
|
||||
}
|
||||
else
|
||||
{
|
||||
login->end = login->text + len - 1;
|
||||
login->text[len - 1] = '\0';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fclose(fp);
|
||||
free(line);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fgets(line, config.max_login_len + 1, fp))
|
||||
{
|
||||
int saved_cur = abs(atoi(line));
|
||||
|
||||
if (saved_cur < desktop->len)
|
||||
{
|
||||
desktop->cur = saved_cur;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
free(line);
|
||||
}
|
||||
15
src/utils.h
Normal file
15
src/utils.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef H_LY_UTILS
|
||||
#define H_LY_UTILS
|
||||
|
||||
#include "draw.h"
|
||||
#include "inputs.h"
|
||||
#include "config.h"
|
||||
|
||||
void desktop_load(struct desktop* target);
|
||||
void hostname(char** out);
|
||||
void free_hostname();
|
||||
void switch_tty(struct term_buf* buf);
|
||||
void save(struct desktop* desktop, struct text* login);
|
||||
void load(struct desktop* desktop, struct text* login);
|
||||
|
||||
#endif
|
||||
1
sub/argoat
Submodule
1
sub/argoat
Submodule
Submodule sub/argoat added at 76d1e23b5e
1
sub/configator
Submodule
1
sub/configator
Submodule
Submodule sub/configator added at 8cec178619
1
sub/ctypes
Submodule
1
sub/ctypes
Submodule
Submodule sub/ctypes added at eb4b36559d
1
sub/dragonfail
Submodule
1
sub/dragonfail
Submodule
Submodule sub/dragonfail added at 0a2492c6aa
1
sub/termbox_next
Submodule
1
sub/termbox_next
Submodule
Submodule sub/termbox_next added at 23fff64470
Reference in New Issue
Block a user