mirror of
https://github.com/fairyglade/ly.git
synced 2025-12-21 11:44:55 +00:00
Compare commits
26 Commits
JAicewizar
...
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
|
||||||
54
.github/ISSUE_TEMPLATE/bug.yml
vendored
54
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -1,54 +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`
|
|
||||||
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: 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.
|
|
||||||
render: shell
|
|
||||||
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: 119 KiB |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,5 +1,4 @@
|
|||||||
.idea/
|
bin
|
||||||
zig-cache/
|
obj
|
||||||
zig-out/
|
.gitmodules
|
||||||
valgrind.log
|
valgrind.log
|
||||||
.zig-cache
|
|
||||||
|
|||||||
476
build.zig
476
build.zig
@@ -1,476 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const builtin = @import("builtin");
|
|
||||||
|
|
||||||
const PatchMap = std.StringHashMap([]const u8);
|
|
||||||
|
|
||||||
const min_zig_string = "0.12.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 = 0 };
|
|
||||||
|
|
||||||
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 default_tty_str: []const u8 = undefined;
|
|
||||||
|
|
||||||
const ProgressNode = if (current_zig.minor == 12) *std.Progress.Node else std.Progress.Node;
|
|
||||||
|
|
||||||
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";
|
|
||||||
|
|
||||||
const bin_directory = try b.allocator.dupe(u8, config_directory);
|
|
||||||
config_directory = try std.fs.path.join(b.allocator, &[_][]const u8{ dest_directory, config_directory });
|
|
||||||
|
|
||||||
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", bin_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");
|
|
||||||
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");
|
|
||||||
installexe_step.makeFn = ExeInstaller(true).make;
|
|
||||||
installexe_step.dependOn(b.getInstallStep());
|
|
||||||
|
|
||||||
const installnoconf_step = b.step("installnoconf", "Install Ly without its configuration file");
|
|
||||||
installnoconf_step.makeFn = ExeInstaller(false).make;
|
|
||||||
installnoconf_step.dependOn(b.getInstallStep());
|
|
||||||
|
|
||||||
const installsystemd_step = b.step("installsystemd", "Install the Ly systemd service");
|
|
||||||
installsystemd_step.makeFn = ServiceInstaller(.Systemd).make;
|
|
||||||
installsystemd_step.dependOn(installexe_step);
|
|
||||||
|
|
||||||
const installopenrc_step = b.step("installopenrc", "Install the Ly openrc service");
|
|
||||||
installopenrc_step.makeFn = ServiceInstaller(.Openrc).make;
|
|
||||||
installopenrc_step.dependOn(installexe_step);
|
|
||||||
|
|
||||||
const installrunit_step = b.step("installrunit", "Install the Ly runit service");
|
|
||||||
installrunit_step.makeFn = ServiceInstaller(.Runit).make;
|
|
||||||
installrunit_step.dependOn(installexe_step);
|
|
||||||
|
|
||||||
const installs6_step = b.step("installs6", "Install the Ly s6 service");
|
|
||||||
installs6_step.makeFn = ServiceInstaller(.S6).make;
|
|
||||||
installs6_step.dependOn(installexe_step);
|
|
||||||
|
|
||||||
const installdinit_step = b.step("installdinit", "Install the Ly dinit service");
|
|
||||||
installdinit_step.makeFn = ServiceInstaller(.Dinit).make;
|
|
||||||
installdinit_step.dependOn(installexe_step);
|
|
||||||
|
|
||||||
const uninstallall_step = b.step("uninstallall", "Uninstall Ly and all services");
|
|
||||||
uninstallall_step.makeFn = uninstallall;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ExeInstaller(install_conf: bool) type {
|
|
||||||
return struct {
|
|
||||||
pub fn make(step: *std.Build.Step, _: ProgressNode) !void {
|
|
||||||
try install_ly(step.owner.allocator, install_conf);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const InitSystem = enum {
|
|
||||||
Systemd,
|
|
||||||
Openrc,
|
|
||||||
Runit,
|
|
||||||
S6,
|
|
||||||
Dinit,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn ServiceInstaller(comptime init_system: InitSystem) type {
|
|
||||||
return struct {
|
|
||||||
pub fn make(step: *std.Build.Step, _: ProgressNode) !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);
|
|
||||||
|
|
||||||
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", .{});
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn install_ly(allocator: std.mem.Allocator, install_config: bool) !void {
|
|
||||||
const ly_config_directory = try std.fs.path.join(allocator, &[_][]const u8{ 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{ config_directory, "/ly/lang" });
|
|
||||||
std.fs.cwd().makePath(ly_lang_path) catch {
|
|
||||||
std.debug.print("warn: {s} already exists as a directory.\n", .{config_directory});
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin" });
|
|
||||||
if (!std.mem.eql(u8, dest_directory, "")) {
|
|
||||||
std.fs.cwd().makePath(exe_path) catch {
|
|
||||||
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) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
const patched_config = try patchFile(allocator, "res/config.ini", patch_map);
|
|
||||||
try installText(patched_config, config_dir, ly_config_directory, "config.ini", .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var patch_map = PatchMap.init(allocator);
|
|
||||||
defer patch_map.deinit();
|
|
||||||
|
|
||||||
try patch_map.put("$CONFIG_DIRECTORY", config_directory);
|
|
||||||
|
|
||||||
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" });
|
|
||||||
if (!std.mem.eql(u8, dest_directory, "")) {
|
|
||||||
std.fs.cwd().makePath(pam_path) catch {
|
|
||||||
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 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn uninstallall(step: *std.Build.Step, _: ProgressNode) !void {
|
|
||||||
const allocator = step.owner.allocator;
|
|
||||||
|
|
||||||
try deleteTree(allocator, config_directory, "/ly", "ly config directory not found");
|
|
||||||
|
|
||||||
const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, 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");
|
|
||||||
try deleteFile(allocator, prefix_directory, "/lib/systemd/system/ly.service", "systemd service not found");
|
|
||||||
try deleteFile(allocator, config_directory, "/init.d/ly", "openrc service not found");
|
|
||||||
try deleteTree(allocator, config_directory, "/sv/ly", "runit service not found");
|
|
||||||
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");
|
|
||||||
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,16 +0,0 @@
|
|||||||
.{
|
|
||||||
.name = "ly",
|
|
||||||
.version = "1.0.0",
|
|
||||||
.minimum_zig_version = "0.12.0",
|
|
||||||
.dependencies = .{
|
|
||||||
.clap = .{
|
|
||||||
.url = "https://github.com/Hejsil/zig-clap/archive/refs/tags/0.9.1.tar.gz",
|
|
||||||
.hash = "122062d301a203d003547b414237229b09a7980095061697349f8bef41be9c30266b",
|
|
||||||
},
|
|
||||||
.zigini = .{
|
|
||||||
.url = "https://github.com/Kawaii-Ash/zigini/archive/0bba97a12582928e097f4074cc746c43351ba4c8.tar.gz",
|
|
||||||
.hash = "12209b971367b4066d40ecad4728e6fdffc4cc4f19356d424c2de57f5b69ac7a619a",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.paths = .{""},
|
|
||||||
}
|
|
||||||
3475
include/termbox2.h
3475
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
|
||||||
186
readme.md
186
readme.md
@@ -1,59 +1,39 @@
|
|||||||
|
|
||||||
# Ly - a TUI display manager
|
# Ly - a TUI display manager
|
||||||

|

|
||||||
|
|
||||||
Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD.
|
Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
- Compile-time:
|
- a C99 compiler (tested with tcc and gcc)
|
||||||
- zig >=0.12.0
|
- a C standard library
|
||||||
- libc
|
- GNU make
|
||||||
- pam
|
- pam
|
||||||
- xcb (optional, required by default; needed for X11 support)
|
- xcb
|
||||||
- Runtime (with default config):
|
|
||||||
- xorg
|
- xorg
|
||||||
- xorg-xauth
|
- xorg-xauth
|
||||||
|
- mcookie
|
||||||
|
- tput
|
||||||
- shutdown
|
- shutdown
|
||||||
|
|
||||||
### Debian
|
On Debian-based distros running `apt install build-essential libpam0g-dev libxcb-xkb-dev` as root should install all the dependencies for you.
|
||||||
```
|
|
||||||
# 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
|
|
||||||
```
|
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
The following desktop environments were tested with success:
|
The following desktop environments were tested with success
|
||||||
- awesome
|
|
||||||
- bspwm
|
|
||||||
- budgie
|
- budgie
|
||||||
- cinnamon
|
- cinnamon
|
||||||
- cosmic
|
|
||||||
- deepin
|
- deepin
|
||||||
- dwl
|
|
||||||
- dwm
|
|
||||||
- enlightenment
|
- enlightenment
|
||||||
- gnome
|
- gnome
|
||||||
- i3
|
- i3
|
||||||
- kde
|
- kde
|
||||||
- labwc
|
|
||||||
- lxde
|
- lxde
|
||||||
- lxqt
|
- lxqt
|
||||||
- mate
|
- mate
|
||||||
- maxx
|
|
||||||
- pantheon
|
|
||||||
- qtile
|
|
||||||
- spectrwm
|
|
||||||
- sway
|
- sway
|
||||||
- windowmaker
|
|
||||||
- xfce
|
- xfce
|
||||||
- xmonad
|
- pantheon
|
||||||
|
- maxx
|
||||||
|
- windowmaker
|
||||||
|
|
||||||
Ly should work with any X desktop environment, and provides
|
Ly should work with any X desktop environment, and provides
|
||||||
basic wayland support (sway works very well, for example).
|
basic wayland support (sway works very well, for example).
|
||||||
@@ -67,154 +47,39 @@ changing the source code won't be necessary :)
|
|||||||
## Cloning and Compiling
|
## Cloning and Compiling
|
||||||
Clone the repository
|
Clone the repository
|
||||||
```
|
```
|
||||||
$ git clone https://github.com/fairyglade/ly
|
git clone https://github.com/nullgemm/ly.git
|
||||||
```
|
```
|
||||||
|
|
||||||
Change the directory to ly
|
Fetch submodules
|
||||||
```
|
```
|
||||||
$ cd ly
|
make github
|
||||||
```
|
```
|
||||||
|
|
||||||
Compile
|
Compile
|
||||||
```
|
```
|
||||||
$ zig build
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
Test in the configured tty (tty2 by default)
|
Test in the configured tty (tty2 by default)
|
||||||
or a terminal emulator (but desktop environments won't start)
|
or a terminal emulator (but desktop environments won't start)
|
||||||
```
|
```
|
||||||
# zig build run
|
sudo make run
|
||||||
```
|
```
|
||||||
|
|
||||||
Install Ly and the provided systemd service file
|
Install Ly and the provided systemd service file
|
||||||
```
|
```
|
||||||
# zig build installsystemd
|
sudo make install
|
||||||
```
|
```
|
||||||
|
|
||||||
Enable the service
|
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
|
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
|
disable getty on Ly's tty to prevent "login" from spawning on top of it
|
||||||
```
|
```
|
||||||
# systemctl disable getty@tty2.service
|
sudo 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 installopenrc
|
|
||||||
```
|
|
||||||
|
|
||||||
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 installrunit
|
|
||||||
# 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 installs6
|
|
||||||
```
|
|
||||||
|
|
||||||
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 installdinit
|
|
||||||
# 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 copying the system service and the configuration file. That's
|
|
||||||
called *updating*. To update, simply run:
|
|
||||||
|
|
||||||
```
|
|
||||||
# zig build installnoconf
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want to also copy the default config file (but still not the system service), run:
|
|
||||||
|
|
||||||
```
|
|
||||||
# zig build installexe
|
|
||||||
```
|
|
||||||
|
|
||||||
## Arch Linux Installation
|
|
||||||
You can install ly from the [`[extra]` repos](https://archlinux.org/packages/extra/x86_64/ly/):
|
|
||||||
```
|
|
||||||
$ sudo pacman -S ly
|
|
||||||
```
|
|
||||||
|
|
||||||
## Gentoo Installation
|
|
||||||
You can install ly from the GURU repository:
|
|
||||||
|
|
||||||
Note: If the package is masked, you may need to unmask it using ~amd64 keyword:
|
|
||||||
```bash
|
|
||||||
# echo 'x11-misc/ly ~amd64' >> /etc/portage/package.accept_keywords
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Enable the GURU repository:
|
|
||||||
```bash
|
|
||||||
# eselect repository enable guru
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Sync the GURU repository:
|
|
||||||
```bash
|
|
||||||
# emaint sync -r guru
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Install ly from source:
|
|
||||||
```bash
|
|
||||||
# emerge --ask x11-misc/ly
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
@@ -229,9 +94,11 @@ while on the desktop field (above the login field).
|
|||||||
## .xinitrc
|
## .xinitrc
|
||||||
If your .xinitrc doesn't work make sure it is executable and includes a shebang.
|
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:
|
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.
|
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:
|
On ArchLinux, the example .xinitrc (/etc/X11/xinit/xinitrc) starts like this:
|
||||||
```
|
```
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
@@ -245,10 +112,9 @@ Take a look at your .xsession if X doesn't start, as it can interfere
|
|||||||
|
|
||||||
## PSX DOOM fire animation
|
## PSX DOOM fire animation
|
||||||
To enable the famous PSX DOOM fire described by [Fabien Sanglard](http://fabiensanglard.net/doom_fire_psx/index.html),
|
To enable the famous PSX DOOM fire described by [Fabien Sanglard](http://fabiensanglard.net/doom_fire_psx/index.html),
|
||||||
just set `animation = doom` in `/etc/ly/config.ini`. You may also
|
just uncomment `animate = true` in `/etc/ly/config.ini`. You may also
|
||||||
disable the main box borders with `hide_borders = true`.
|
disable the main box borders with `hide_borders = true`.
|
||||||
|
|
||||||
## Additional Information
|
## Additional Information
|
||||||
The name "Ly" is a tribute to the fairy from the game Rayman.
|
The name "Ly" is a tribute to the fairy from the game Rayman.
|
||||||
Ly was tested by oxodao, who is some seriously awesome dude.
|
Ly was tested by oxodao, who is some seriously awesome dude.
|
||||||
|
|
||||||
|
|||||||
261
res/config.ini
261
res/config.ini
@@ -1,219 +1,104 @@
|
|||||||
# The color settings in Ly take a digit 0-8 corresponding to:
|
# animation enabled
|
||||||
#define TB_DEFAULT 0x00
|
#animate = false
|
||||||
#define TB_BLACK 0x01
|
#animate = true
|
||||||
#define TB_RED 0x02
|
|
||||||
#define TB_GREEN 0x03
|
|
||||||
#define TB_YELLOW 0x04
|
|
||||||
#define TB_BLUE 0x05
|
|
||||||
#define TB_MAGENTA 0x06
|
|
||||||
#define TB_CYAN 0x07
|
|
||||||
#define TB_WHITE 0x08
|
|
||||||
# The default color varies, but usually it makes the background black and the foreground white.
|
|
||||||
# You can also combine these colors with the following style attributes using bitwise OR:
|
|
||||||
#define TB_BOLD 0x0100
|
|
||||||
#define TB_UNDERLINE 0x0200
|
|
||||||
#define TB_REVERSE 0x0400
|
|
||||||
#define TB_ITALIC 0x0800
|
|
||||||
#define TB_BLINK 0x1000
|
|
||||||
#define TB_HI_BLACK 0x2000
|
|
||||||
#define TB_BRIGHT 0x4000
|
|
||||||
#define TB_DIM 0x8000
|
|
||||||
# For example, to set the foreground color to red and bold, you would do 0x02 | 0x0100 = 0x0102.
|
|
||||||
# Note that you must pre-calculate the value because Ly doesn't parse bitwise OR operations in its config.
|
|
||||||
#
|
|
||||||
# Moreover, to set the VT color palette, you are encouraged to use another tool such as
|
|
||||||
# mkinitcpio-colors (https://github.com/evanpurkhiser/mkinitcpio-colors). Note that the color palette defined with
|
|
||||||
# mkinitcpio-colors takes 16 colors (0-15), only values 0-8 are valid with Ly and these values do not correspond
|
|
||||||
# exactly. For instance, in defining palettes with mkinitcpio-colors, the order is black, dark red, dark green, brown, dark
|
|
||||||
# blue, dark purple, dark cyan, light gray, dark gray, bright red, bright green, yellow, bright blue, bright purple, bright
|
|
||||||
# cyan, and white, indexed in that order 0 through 15. For example, the color defined for white (indexed at 15 in the mkinitcpio
|
|
||||||
# config) will be used by Ly for fg = 0x0008.
|
|
||||||
|
|
||||||
# The active animation
|
# the active animation (only animation '0' available for now)
|
||||||
# none -> Nothing
|
#animation = 0
|
||||||
# doom -> PSX DOOM fire
|
|
||||||
# matrix -> CMatrix
|
|
||||||
animation = none
|
|
||||||
|
|
||||||
# Stop the animation after some time
|
# the char used to mask the password
|
||||||
# 0 -> Run forever
|
#asterisk = *
|
||||||
# 1..2e12 -> Stop the animation after this many seconds
|
#asterisk = o
|
||||||
animation_timeout_sec = 0
|
|
||||||
|
|
||||||
# The character used to mask the password
|
# background color id
|
||||||
# If null, the password will be hidden
|
#bg = 0
|
||||||
# Note: you can use a # by escaping it like so: \#
|
|
||||||
asterisk = *
|
|
||||||
|
|
||||||
# The number of failed authentications before a special animation is played... ;)
|
# blank main box
|
||||||
auth_fails = 10
|
#blank_box = true
|
||||||
|
|
||||||
# Background color id
|
# erase password input on failure
|
||||||
bg = 0x0000
|
#blank_password = false
|
||||||
|
#blank_password = true
|
||||||
|
|
||||||
# Change the state and language of the big clock
|
# console path
|
||||||
# none -> Disabled (default)
|
#console_dev = /dev/console
|
||||||
# en -> English
|
|
||||||
# fa -> Farsi
|
|
||||||
bigclock = none
|
|
||||||
|
|
||||||
# Blank main box background
|
# input active by default on startup
|
||||||
# Setting to false will make it transparent
|
#default_input = 2
|
||||||
blank_box = true
|
|
||||||
|
|
||||||
# Border foreground color id
|
# foreground color id
|
||||||
border_fg = 0x0008
|
#fg = 9
|
||||||
|
|
||||||
# Title to show at the top of the main box
|
# remove main box borders
|
||||||
# If set to null, none will be shown
|
#hide_borders = false
|
||||||
box_title = null
|
#hide_borders = true
|
||||||
|
|
||||||
# Brightness increase command
|
# number of visible chars on an input
|
||||||
brightness_down_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q s 10%-
|
#input_len = 34
|
||||||
|
|
||||||
# Brightness decrease key
|
# active language
|
||||||
brightness_down_key = F5
|
#lang = en
|
||||||
|
#lang = fr
|
||||||
|
|
||||||
# Brightness increase command
|
# load the saved desktop and login
|
||||||
brightness_up_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q s +10%
|
#load = true
|
||||||
|
|
||||||
# Brightness increase key
|
# main box margins
|
||||||
brightness_up_key = F6
|
#margin_box_h = 2
|
||||||
|
#margin_box_v = 1
|
||||||
|
|
||||||
# Erase password input on failure
|
# total input sizes
|
||||||
clear_password = false
|
#max_desktop_len = 100
|
||||||
|
#max_login_len = 255
|
||||||
|
#max_password_len = 255
|
||||||
|
|
||||||
# Format string for clock in top right corner (see strftime specification). Example: %c
|
# cookie generator
|
||||||
# If null, the clock won't be shown
|
#mcookie_cmd = /usr/bin/mcookie
|
||||||
clock = null
|
|
||||||
|
|
||||||
# CMatrix animation foreground color id
|
# event timeout in milliseconds
|
||||||
cmatrix_fg = 0x0003
|
#min_refresh_delta = 5
|
||||||
|
|
||||||
# Console path
|
# default path
|
||||||
console_dev = /dev/console
|
#path = /sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin
|
||||||
|
|
||||||
# Input box active by default on startup
|
# command executed when pressing F2
|
||||||
# Available inputs: info_line, session, login, password
|
#restart_cmd = /sbin/shutdown -r now
|
||||||
default_input = login
|
|
||||||
|
|
||||||
# Error background color id
|
# save the current desktop and login as defaults
|
||||||
error_bg = 0x0000
|
#save = true
|
||||||
|
|
||||||
# Error foreground color id
|
# file in which to save and load the default desktop and login
|
||||||
# Default is red and bold: TB_RED | TB_BOLD
|
#save_file = /etc/ly/save
|
||||||
error_fg = 0x0102
|
|
||||||
|
|
||||||
# Foreground color id
|
# service name (set to ly to use the provided pam config file)
|
||||||
fg = 0x0008
|
#service_name = ly
|
||||||
|
|
||||||
# Remove main box borders
|
# command executed when pressing F1
|
||||||
hide_borders = false
|
#shutdown_cmd = /sbin/shutdown -a now
|
||||||
|
|
||||||
# Remove power management command hints
|
# terminal reset command (tput is faster)
|
||||||
hide_key_hints = false
|
#term_reset_cmd = /usr/bin/tput reset
|
||||||
|
|
||||||
# Initial text to show on the info line
|
# tty in use
|
||||||
# If set to null, the info line defaults to the hostname
|
#tty = 2
|
||||||
initial_info_text = null
|
|
||||||
|
|
||||||
# Input boxes length
|
# wayland setup command
|
||||||
input_len = 34
|
#wayland_cmd = /etc/ly/wsetup.sh
|
||||||
|
|
||||||
# Active language
|
# add wayland specifier to session names
|
||||||
# Available languages are found in $CONFIG_DIRECTORY/ly/lang/
|
#wayland_specifier = false
|
||||||
lang = en
|
#wayland_specifier = true
|
||||||
|
|
||||||
# Load the saved desktop and username
|
# wayland desktop environments
|
||||||
load = true
|
#waylandsessions = /usr/share/wayland-sessions
|
||||||
|
|
||||||
# Command executed when logging in
|
# xorg server command
|
||||||
# If null, no command will be executed
|
#x_cmd = /usr/bin/X
|
||||||
# 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
|
# xorg setup command
|
||||||
# If null, no command will be executed
|
#x_cmd_setup = /etc/ly/xsetup.sh
|
||||||
# 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
|
# xorg xauthority edition tool
|
||||||
margin_box_h = 2
|
#xauth_cmd = /usr/bin/xauth
|
||||||
|
|
||||||
# Main box vertical margin
|
# xorg desktop environments
|
||||||
margin_box_v = 1
|
#xsessions = /usr/share/xsessions
|
||||||
|
|
||||||
# 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 = /sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/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 X11 and Wayland sessions
|
|
||||||
# By default it's saved in the user's home directory
|
|
||||||
# Note: this file won't be used in a shell session (due to the need of stdout and stderr)
|
|
||||||
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
|
|
||||||
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
|
|
||||||
xsessions = $PREFIX_DIRECTORY/share/xsessions
|
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
capslock = Bloq Majús
|
|
||||||
err_alloc = falla d'assignació de memòria
|
|
||||||
err_bounds = índex fora de límit
|
|
||||||
err_chdir = error al obrir carpeta home
|
|
||||||
err_console_dev = error al accedir a la consola
|
|
||||||
err_dgn_oob = missatge de registre
|
|
||||||
err_domain = domini invàlid
|
|
||||||
err_hostname = error al obtenir el nom del host
|
|
||||||
err_mlock = error al bloquejar la clau de memòria
|
|
||||||
err_null = punter nul
|
|
||||||
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 al obtenir informació de l'usuari
|
|
||||||
err_pam_authok_reqd = token expirat
|
|
||||||
err_pam_buf = error de la memòria intermitja
|
|
||||||
err_pam_cred_err = error al establir les credencials
|
|
||||||
err_pam_cred_expired = credencials expirades
|
|
||||||
err_pam_cred_insufficient = credencials insuficients
|
|
||||||
err_pam_cred_unavail = error al obtenir credencials
|
|
||||||
err_pam_maxtries = s'ha assolit al màxim nombre 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 al establir la ruta
|
|
||||||
err_perm_dir = error al canviar de directori actual
|
|
||||||
err_perm_group = error al degradar els permisos de grup
|
|
||||||
err_perm_user = error al degradar els permisos de l'usuari
|
|
||||||
err_pwnam = error al obtenir la informació de l'usuari
|
|
||||||
err_user_gid = error al establir el GID de l'usuari
|
|
||||||
err_user_init = error al inicialitzar usuari
|
|
||||||
err_user_uid = error al establir el UID de l'usuari
|
|
||||||
err_xsessions_dir = error al cercar la carpeta de sessions
|
|
||||||
err_xsessions_open = error al obrir la carpeta de sessions
|
|
||||||
login = iniciar sessió
|
|
||||||
logout = tancar sessió
|
|
||||||
numlock = Bloq Num
|
|
||||||
password = Clau
|
|
||||||
restart = reiniciar
|
|
||||||
shell = shell
|
|
||||||
shutdown = aturar
|
|
||||||
wayland = wayland
|
|
||||||
xinitrc = xinitrc
|
|
||||||
@@ -1,45 +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,45 +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,19 +1,13 @@
|
|||||||
authenticating = authenticating...
|
|
||||||
brightness_down = decrease brightness
|
|
||||||
brightness_up = increase brightness
|
|
||||||
capslock = capslock
|
capslock = capslock
|
||||||
err_alloc = failed memory allocation
|
err_alloc = failed memory allocation
|
||||||
err_bounds = out-of-bounds index
|
err_bounds = out-of-bounds index
|
||||||
err_brightness_change = failed to change brightness
|
|
||||||
err_chdir = failed to open home folder
|
err_chdir = failed to open home folder
|
||||||
err_console_dev = failed to access console
|
err_console_dev = failed to access console
|
||||||
err_dgn_oob = log message
|
err_dgn_oob = log message
|
||||||
err_domain = invalid domain
|
err_domain = invalid domain
|
||||||
err_envlist = failed to get envlist
|
|
||||||
err_hostname = failed to get hostname
|
err_hostname = failed to get hostname
|
||||||
err_mlock = failed to lock password memory
|
err_mlock = failed to lock password memory
|
||||||
err_null = null pointer
|
err_null = null pointer
|
||||||
err_numlock = failed to set numlock
|
|
||||||
err_pam = pam transaction failed
|
err_pam = pam transaction failed
|
||||||
err_pam_abort = pam transaction aborted
|
err_pam_abort = pam transaction aborted
|
||||||
err_pam_acct_expired = account expired
|
err_pam_acct_expired = account expired
|
||||||
@@ -35,25 +29,17 @@ err_perm_dir = failed to change current directory
|
|||||||
err_perm_group = failed to downgrade group permissions
|
err_perm_group = failed to downgrade group permissions
|
||||||
err_perm_user = failed to downgrade user permissions
|
err_perm_user = failed to downgrade user permissions
|
||||||
err_pwnam = failed to get user info
|
err_pwnam = failed to get user info
|
||||||
err_unknown = an unknown error occurred
|
|
||||||
err_user_gid = failed to set user GID
|
err_user_gid = failed to set user GID
|
||||||
err_user_init = failed to initialize user
|
err_user_init = failed to initialize user
|
||||||
err_user_uid = failed to set user UID
|
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_dir = failed to find sessions folder
|
||||||
err_xsessions_open = failed to open sessions folder
|
err_xsessions_open = failed to open sessions folder
|
||||||
insert = insert
|
f1 = F1 shutdown
|
||||||
login = login
|
f2 = F2 reboot
|
||||||
|
login = login:
|
||||||
logout = logged out
|
logout = logged out
|
||||||
normal = normal
|
|
||||||
no_x11_support = x11 support disabled at compile-time
|
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
password = password
|
password = password:
|
||||||
restart = reboot
|
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = shutdown
|
|
||||||
sleep = sleep
|
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
x11 = x11
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
authenticating = autenticando...
|
|
||||||
brightness_down = bajar brillo
|
|
||||||
brightness_up = subir brillo
|
|
||||||
capslock = Bloq Mayús
|
capslock = Bloq Mayús
|
||||||
err_alloc = asignación de memoria fallida
|
err_alloc = asignación de memoria fallida
|
||||||
err_bounds = índice fuera de límites
|
err_bounds = índice fuera de límites
|
||||||
@@ -18,7 +15,7 @@ err_pam_auth = error de autenticación
|
|||||||
err_pam_authinfo_unavail = error al obtener información del usuario
|
err_pam_authinfo_unavail = error al obtener información del usuario
|
||||||
err_pam_authok_reqd = token expirado
|
err_pam_authok_reqd = token expirado
|
||||||
err_pam_buf = error de la memoria intermedia
|
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_expired = credenciales expiradas
|
||||||
err_pam_cred_insufficient = credenciales insuficientes
|
err_pam_cred_insufficient = credenciales insuficientes
|
||||||
err_pam_cred_unavail = error al obtener credenciales
|
err_pam_cred_unavail = error al obtener credenciales
|
||||||
@@ -33,21 +30,16 @@ err_perm_group = error al degradar los permisos del grupo
|
|||||||
err_perm_user = error al degradar los permisos del usuario
|
err_perm_user = error al degradar los permisos del usuario
|
||||||
err_pwnam = error al obtener la información del usuario
|
err_pwnam = error al obtener la información del usuario
|
||||||
err_user_gid = error al establecer el GID del usuario
|
err_user_gid = error al establecer el GID del usuario
|
||||||
err_user_init = error al inicializar usuario
|
err_user_init = errpr al inicializar usuario
|
||||||
err_user_uid = error al establecer el UID del usuario
|
err_user_uid = error al establecer el UID del usuario
|
||||||
err_xsessions_dir = error al buscar la carpeta de sesiones
|
err_xsessions_dir = error al buscar la carpeta de sesiones
|
||||||
err_xsessions_open = error al abrir la carpeta de sesiones
|
err_xsessions_open = error al abrir la carpeta de sesiones
|
||||||
insert = insertar
|
f1 = F1 apagar
|
||||||
login = usuario
|
f2 = F2 reiniciar
|
||||||
logout = cerrar sesión
|
login = iniciar sesion:
|
||||||
no_x11_support = soporte para x11 deshabilitado en tiempo de compilación
|
logout = cerrar sesion
|
||||||
normal = normal
|
|
||||||
numlock = Bloq Num
|
numlock = Bloq Num
|
||||||
other = otro
|
password = contraseña:
|
||||||
password = contraseña
|
|
||||||
restart = reiniciar
|
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = apagar
|
|
||||||
sleep = suspender
|
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -1,25 +1,19 @@
|
|||||||
authenticating = authentification...
|
capslock = verr.maj
|
||||||
brightness_down = diminuer la luminosité
|
|
||||||
brightness_up = augmenter la luminosité
|
|
||||||
capslock = verr.maj
|
|
||||||
err_alloc = échec d'allocation mémoire
|
err_alloc = échec d'allocation mémoire
|
||||||
err_bounds = indice hors-limite
|
err_bounds = indice hors-limite
|
||||||
err_brightness_change = échec du changement de luminosité
|
|
||||||
err_chdir = échec de l'ouverture du répertoire home
|
err_chdir = échec de l'ouverture du répertoire home
|
||||||
err_console_dev = échec d'accès à la console
|
err_console_dev = échec d'accès à la console
|
||||||
err_dgn_oob = message
|
err_dgn_oob = message
|
||||||
err_domain = domaine invalide
|
err_domain = domaine invalide
|
||||||
err_envlist = échec de lecture de la liste d'environnement
|
err_hostname = échec de captation du nom d'hôte
|
||||||
err_hostname = échec de lecture du nom d'hôte
|
|
||||||
err_mlock = échec du verrouillage mémoire
|
err_mlock = échec du verrouillage mémoire
|
||||||
err_null = pointeur null
|
err_null = pointeur null
|
||||||
err_numlock = échec de modification du verr.num
|
|
||||||
err_pam = échec de la transaction pam
|
err_pam = échec de la transaction pam
|
||||||
err_pam_abort = transaction pam avortée
|
err_pam_abort = transaction pam avortée
|
||||||
err_pam_acct_expired = compte expiré
|
err_pam_acct_expired = compte expiré
|
||||||
err_pam_auth = erreur d'authentification
|
err_pam_auth = erreur d'authentification
|
||||||
err_pam_authinfo_unavail = échec de l'obtention des infos utilisateur
|
|
||||||
err_pam_authok_reqd = tiquet expiré
|
err_pam_authok_reqd = tiquet expiré
|
||||||
|
err_pam_authinfo_unavail = échec de l'obtention des infos utilisateur
|
||||||
err_pam_buf = erreur de mémoire tampon
|
err_pam_buf = erreur de mémoire tampon
|
||||||
err_pam_cred_err = échec de la modification des identifiants
|
err_pam_cred_err = échec de la modification des identifiants
|
||||||
err_pam_cred_expired = identifiants expirés
|
err_pam_cred_expired = identifiants expirés
|
||||||
@@ -34,26 +28,18 @@ err_path = échec de la modification du path
|
|||||||
err_perm_dir = échec de changement de répertoire
|
err_perm_dir = échec de changement de répertoire
|
||||||
err_perm_group = échec du déclassement des permissions de groupe
|
err_perm_group = échec du déclassement des permissions de groupe
|
||||||
err_perm_user = échec du déclassement des permissions utilisateur
|
err_perm_user = échec du déclassement des permissions utilisateur
|
||||||
err_pwnam = échec de lecture des infos utilisateur
|
err_pwnam = échec de captation des infos utilisateur
|
||||||
err_unknown = une erreur inconnue est survenue
|
|
||||||
err_user_gid = échec de modification du GID
|
err_user_gid = échec de modification du GID
|
||||||
err_user_init = échec d'initialisation de l'utilisateur
|
err_user_init = échec d'initialisation de l'utilisateur
|
||||||
err_user_uid = échec de modification du UID
|
err_user_uid = échec de modification du UID
|
||||||
err_xauth = échec de la commande xauth
|
|
||||||
err_xcb_conn = échec de la connexion xcb
|
|
||||||
err_xsessions_dir = échec de la recherche du dossier de sessions
|
err_xsessions_dir = échec de la recherche du dossier de sessions
|
||||||
err_xsessions_open = échec de l'ouverture du dossier de sessions
|
err_xsessions_open = échec de l'ouverture du dossier de sessions
|
||||||
insert = insertion
|
f1 = F1 éteindre
|
||||||
login = identifiant
|
f2 = F2 redémarrer
|
||||||
logout = déconnecté
|
login = identifiant :
|
||||||
normal = normal
|
logout = déconnection
|
||||||
no_x11_support = support pour x11 désactivé lors de la compilation
|
|
||||||
numlock = verr.num
|
numlock = verr.num
|
||||||
password = mot de passe
|
password = mot de passe :
|
||||||
restart = redémarrer
|
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = éteindre
|
|
||||||
sleep = veille
|
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
x11 = x11
|
|
||||||
|
|||||||
@@ -1,45 +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,45 +0,0 @@
|
|||||||
capslock = capslock
|
|
||||||
err_alloc = nieudana alokacja pamięci
|
|
||||||
err_bounds = indeks poza granicami
|
|
||||||
err_chdir = nie udało się otworzyć folderu domowego
|
|
||||||
err_console_dev = nie udało się uzyskać dostępu do konsoli
|
|
||||||
err_dgn_oob = wiadomość loga
|
|
||||||
err_domain = niepoprawna domena
|
|
||||||
err_hostname = nie udało się uzyskać nazwy hosta
|
|
||||||
err_mlock = nie udało się zablokować pamięci haseł
|
|
||||||
err_null = wskaźnik zerowy
|
|
||||||
err_pam = transakcja pam nieudana
|
|
||||||
err_pam_abort = transakcja pam przerwana
|
|
||||||
err_pam_acct_expired = konto wygasło
|
|
||||||
err_pam_auth = błąd autentyfikacji
|
|
||||||
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 = brak uprawnień
|
|
||||||
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_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_xsessions_dir = nie udało się znaleźć folderu sesji
|
|
||||||
err_xsessions_open = nie udało się otworzyć folderu sesji
|
|
||||||
login = login
|
|
||||||
logout = wylogowano
|
|
||||||
numlock = numlock
|
|
||||||
password = hasło
|
|
||||||
restart = uruchom ponownie
|
|
||||||
shell = powłoka
|
|
||||||
shutdown = wyłącz
|
|
||||||
wayland = wayland
|
|
||||||
xinitrc = xinitrc
|
|
||||||
@@ -1,45 +1,45 @@
|
|||||||
capslock = capslock
|
capslock = caixa alta
|
||||||
err_alloc = erro na atribuição de memória
|
err_alloc = alocação de memória malsucedida
|
||||||
err_bounds = índice fora de limites
|
err_bounds = índice fora de limites
|
||||||
err_chdir = erro ao abrir a pasta home
|
err_chdir = não foi possível abrir o diretório home
|
||||||
err_console_dev = erro ao aceder à consola
|
err_console_dev = não foi possível acessar o console
|
||||||
err_dgn_oob = mensagem de registo
|
err_dgn_oob = mensagem de log
|
||||||
err_domain = domínio inválido
|
err_domain = domínio inválido
|
||||||
err_hostname = erro ao obter o nome do host
|
err_hostname = não foi possível obter o nome do host
|
||||||
err_mlock = erro de bloqueio de memória
|
err_mlock = bloqueio da memória de senha malsucedido
|
||||||
err_null = ponteiro nulo
|
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_abort = transação pam abortada
|
||||||
err_pam_acct_expired = conta expirada
|
err_pam_acct_expired = conta expirada
|
||||||
err_pam_auth = erro de autenticação
|
err_pam_auth = erro de autenticação
|
||||||
err_pam_authinfo_unavail = erro ao obter informação do utilizador
|
err_pam_authinfo_unavail = não foi possível obter informações do usuário
|
||||||
err_pam_authok_reqd = token expirado
|
err_pam_authok_reqd = token expirada
|
||||||
err_pam_buf = erro de buffer de memória
|
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_expired = credenciais expiradas
|
||||||
err_pam_cred_insufficient = credenciais insuficientes
|
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_maxtries = limite máximo de tentativas atingido
|
||||||
err_pam_perm_denied = permissão negada
|
err_pam_perm_denied = permissão negada
|
||||||
err_pam_session = erro de sessão
|
err_pam_session = erro de sessão
|
||||||
err_pam_sys = erro de sistema
|
err_pam_sys = erro de sistema
|
||||||
err_pam_user_unknown = utilizador desconhecido
|
err_pam_user_unknown = usuário desconhecido
|
||||||
err_path = erro ao definir o caminho de acesso
|
err_path = não foi possível definir o caminho
|
||||||
err_perm_dir = erro ao alterar o diretório atual
|
err_perm_dir = não foi possível alterar o diretório atual
|
||||||
err_perm_group = erro ao reduzir as permissões do grupo
|
err_perm_group = não foi possível reduzir as permissões de grupo
|
||||||
err_perm_user = erro ao reduzir as permissões do utilizador
|
err_perm_user = não foi possível reduzir as permissões de usuário
|
||||||
err_pwnam = erro ao obter informação do utilizador
|
err_pwnam = não foi possível obter informações do usuário
|
||||||
err_user_gid = erro ao definir o GID do utilizador
|
err_user_gid = não foi possível definir o GID do usuário
|
||||||
err_user_init = erro ao iniciar o utilizador
|
err_user_init = não foi possível iniciar o usuário
|
||||||
err_user_uid = erro ao definir o UID do utilizador
|
err_user_uid = não foi possível definir o UID do usuário
|
||||||
err_xsessions_dir = erro ao localizar a pasta das sessões
|
err_xsessions_dir = não foi possível encontrar a pasta das sessões
|
||||||
err_xsessions_open = erro ao abrir a pasta das sessões
|
err_xsessions_open = não foi possível abrir a pasta das sessões
|
||||||
login = iniciar sessão
|
f1 = F1 desligar
|
||||||
logout = terminar sessão
|
f2 = F2 reiniciar
|
||||||
|
login = conectar:
|
||||||
|
logout = desconectado
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
password = palavra-passe
|
password = senha:
|
||||||
restart = reiniciar
|
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = encerrar
|
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -1,45 +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
|
|
||||||
@@ -34,12 +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
|
logout = opreşte sesiunea
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
password = parolă
|
password = parolă:
|
||||||
restart = resetează
|
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = opreşte sistemul
|
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -10,17 +10,17 @@ err_mlock = сбой блокировки памяти
|
|||||||
err_null = нулевой указатель
|
err_null = нулевой указатель
|
||||||
err_pam = pam транзакция не удалась
|
err_pam = pam транзакция не удалась
|
||||||
err_pam_abort = pam транзакция прервана
|
err_pam_abort = pam транзакция прервана
|
||||||
err_pam_acct_expired = срок действия аккаунта истёк
|
err_pam_acct_expired = срок действия аккаунта истек
|
||||||
err_pam_auth = ошибка аутентификации
|
err_pam_auth = ошибка аутентификации
|
||||||
err_pam_authinfo_unavail = не удалось получить информацию о пользователе
|
err_pam_authinfo_unavail = не удалось получить информацию о пользователе
|
||||||
err_pam_authok_reqd = токен истёк
|
err_pam_authok_reqd = токен истек
|
||||||
err_pam_buf = ошибка буфера памяти
|
err_pam_buf = ошибка буфера памяти
|
||||||
err_pam_cred_err = не удалось установить полномочия
|
err_pam_cred_err = не удалось установить полномочия
|
||||||
err_pam_cred_expired = полномочия истекли
|
err_pam_cred_expired = полномочия истекли
|
||||||
err_pam_cred_insufficient = недостаточно полномочий
|
err_pam_cred_insufficient = недостаточо полномочий
|
||||||
err_pam_cred_unavail = не удалось получить полномочия
|
err_pam_cred_unavail = не удалось получить полномочия
|
||||||
err_pam_maxtries = лимит попыток исчерпан
|
err_pam_maxtries = лимит попыток исчерпан
|
||||||
err_pam_perm_denied = доступ запрещён
|
err_pam_perm_denied = доступ запрещен
|
||||||
err_pam_session = ошибка сессии
|
err_pam_session = ошибка сессии
|
||||||
err_pam_sys = системная ошибка
|
err_pam_sys = системная ошибка
|
||||||
err_pam_user_unknown = неизвестный пользователь
|
err_pam_user_unknown = неизвестный пользователь
|
||||||
@@ -34,12 +34,12 @@ err_user_init = не удалось инициализировать польз
|
|||||||
err_user_uid = не удалось установить UID пользователя
|
err_user_uid = не удалось установить UID пользователя
|
||||||
err_xsessions_dir = не удалось найти сессионную папку
|
err_xsessions_dir = не удалось найти сессионную папку
|
||||||
err_xsessions_open = не удалось открыть сессионную папку
|
err_xsessions_open = не удалось открыть сессионную папку
|
||||||
login = логин
|
f1 = F1 выключить
|
||||||
|
f2 = F2 перезагрузить
|
||||||
|
login = логин:
|
||||||
logout = logged out
|
logout = logged out
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
password = пароль
|
password = пароль:
|
||||||
restart = перезагрузить
|
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = выключить
|
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -1,45 +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,45 +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,45 +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,45 +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,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]
|
[Unit]
|
||||||
Description=TUI display manager
|
Description=TUI display manager
|
||||||
After=systemd-user-sessions.service plymouth-quit-wait.service
|
After=systemd-user-sessions.service plymouth-quit-wait.service
|
||||||
After=getty@tty$DEFAULT_TTY.service
|
After=getty@tty2.service
|
||||||
Conflicts=getty@tty$DEFAULT_TTY.service
|
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=idle
|
Type=idle
|
||||||
ExecStart=$PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME
|
ExecStart=/usr/bin/ly
|
||||||
StandardInput=tty
|
StandardInput=tty
|
||||||
TTYPath=/dev/tty$DEFAULT_TTY
|
TTYPath=/dev/tty2
|
||||||
TTYReset=yes
|
TTYReset=yes
|
||||||
TTYVHangup=yes
|
TTYVHangup=yes
|
||||||
|
|
||||||
|
|||||||
12
res/pam.d/ly
12
res/pam.d/ly
@@ -1,18 +1,6 @@
|
|||||||
#%PAM-1.0
|
#%PAM-1.0
|
||||||
|
|
||||||
auth sufficient pam_unix.so try_first_pass likeauth nullok # empty-password will not pass this, but will not fail causing the next line to get executed
|
|
||||||
-auth sufficient pam_fprintd.so # We do not want to get errors when pam_fprintd.so is not present
|
|
||||||
auth include login
|
auth include login
|
||||||
-auth optional pam_gnome_keyring.so
|
|
||||||
-auth optional pam_kwallet5.so
|
|
||||||
|
|
||||||
account include login
|
account include login
|
||||||
|
|
||||||
password include login
|
password include login
|
||||||
-password optional pam_gnome_keyring.so use_authtok
|
|
||||||
|
|
||||||
-session optional pam_systemd.so class=greeter
|
|
||||||
-session optional pam_elogind.so
|
|
||||||
session include login
|
session include login
|
||||||
-session optional pam_gnome_keyring.so auto_start
|
|
||||||
-session optional pam_kwallet5.so auto_start
|
|
||||||
|
|||||||
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 [ -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
|
|
||||||
|
|
||||||
if [ -f "$USERXSESSION" ]; then
|
|
||||||
. "$USERXSESSION"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec "$@"
|
|
||||||
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,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.mem.page_size) 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,92 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
|
||||||
const utils = @import("../tui/utils.zig");
|
|
||||||
|
|
||||||
const interop = @import("../interop.zig");
|
|
||||||
const termbox = interop.termbox;
|
|
||||||
|
|
||||||
const Doom = @This();
|
|
||||||
|
|
||||||
pub const STEPS = 13;
|
|
||||||
pub const FIRE = [_]utils.Cell{
|
|
||||||
utils.initCell(' ', 9, 0),
|
|
||||||
utils.initCell(0x2591, 2, 0), // Red
|
|
||||||
utils.initCell(0x2592, 2, 0), // Red
|
|
||||||
utils.initCell(0x2593, 2, 0), // Red
|
|
||||||
utils.initCell(0x2588, 2, 0), // Red
|
|
||||||
utils.initCell(0x2591, 4, 2), // Yellow
|
|
||||||
utils.initCell(0x2592, 4, 2), // Yellow
|
|
||||||
utils.initCell(0x2593, 4, 2), // Yellow
|
|
||||||
utils.initCell(0x2588, 4, 2), // Yellow
|
|
||||||
utils.initCell(0x2591, 8, 4), // White
|
|
||||||
utils.initCell(0x2592, 8, 4), // White
|
|
||||||
utils.initCell(0x2593, 8, 4), // White
|
|
||||||
utils.initCell(0x2588, 8, 4), // White
|
|
||||||
};
|
|
||||||
|
|
||||||
allocator: Allocator,
|
|
||||||
terminal_buffer: *TerminalBuffer,
|
|
||||||
buffer: []u8,
|
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer) !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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: Doom) void {
|
|
||||||
self.allocator.free(self.buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn realloc(self: *Doom) !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;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw(self: Doom) void {
|
|
||||||
for (0..self.terminal_buffer.width) |x| {
|
|
||||||
for (1..self.terminal_buffer.height) |y| {
|
|
||||||
const source = y * self.terminal_buffer.width + x;
|
|
||||||
const random = (self.terminal_buffer.random.int(u16) % 7) & 3;
|
|
||||||
|
|
||||||
var dest = (source - @min(source, random)) + 1;
|
|
||||||
if (self.terminal_buffer.width > dest) dest = 0 else dest -= self.terminal_buffer.width;
|
|
||||||
|
|
||||||
const buffer_source = self.buffer[source];
|
|
||||||
const buffer_dest_offset = random & 1;
|
|
||||||
|
|
||||||
if (buffer_source < buffer_dest_offset) continue;
|
|
||||||
|
|
||||||
var buffer_dest = buffer_source - buffer_dest_offset;
|
|
||||||
if (buffer_dest > 12) buffer_dest = 0;
|
|
||||||
self.buffer[dest] = @intCast(buffer_dest);
|
|
||||||
|
|
||||||
self.terminal_buffer.buffer[dest] = toTermboxCell(FIRE[buffer_dest]);
|
|
||||||
self.terminal_buffer.buffer[source] = toTermboxCell(FIRE[buffer_source]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn initBuffer(buffer: []u8, width: usize) void {
|
|
||||||
const length = buffer.len - width;
|
|
||||||
const slice_start = buffer[0..length];
|
|
||||||
const slice_end = buffer[length..];
|
|
||||||
|
|
||||||
@memset(slice_start, 0);
|
|
||||||
@memset(slice_end, STEPS - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn toTermboxCell(cell: utils.Cell) termbox.tb_cell {
|
|
||||||
return .{
|
|
||||||
.ch = cell.ch,
|
|
||||||
.fg = cell.fg,
|
|
||||||
.bg = cell.bg,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const Random = std.rand.Random;
|
|
||||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
|
||||||
|
|
||||||
const interop = @import("../interop.zig");
|
|
||||||
const termbox = interop.termbox;
|
|
||||||
|
|
||||||
pub const FRAME_DELAY: u64 = 8;
|
|
||||||
|
|
||||||
// Allowed codepoints
|
|
||||||
pub const MIN_CODEPOINT: isize = 33;
|
|
||||||
pub const MAX_CODEPOINT: isize = 123 - MIN_CODEPOINT;
|
|
||||||
|
|
||||||
// Characters change mid-scroll
|
|
||||||
pub const MID_SCROLL_CHANGE = true;
|
|
||||||
|
|
||||||
const Matrix = @This();
|
|
||||||
|
|
||||||
pub const Dot = struct {
|
|
||||||
value: isize,
|
|
||||||
is_head: bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Line = struct {
|
|
||||||
space: isize,
|
|
||||||
length: isize,
|
|
||||||
update: isize,
|
|
||||||
};
|
|
||||||
|
|
||||||
allocator: Allocator,
|
|
||||||
terminal_buffer: *TerminalBuffer,
|
|
||||||
dots: []Dot,
|
|
||||||
lines: []Line,
|
|
||||||
frame: u64,
|
|
||||||
count: u64,
|
|
||||||
fg_ini: u16,
|
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg_ini: 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_ini = fg_ini,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: Matrix) void {
|
|
||||||
self.allocator.free(self.dots);
|
|
||||||
self.allocator.free(self.lines);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn realloc(self: *Matrix) !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;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub 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 == -1 and self.dots[self.terminal_buffer.width + x].value == ' ') {
|
|
||||||
if (line.space > 0) {
|
|
||||||
line.space -= 1;
|
|
||||||
} else {
|
|
||||||
const randint = self.terminal_buffer.random.int(i16);
|
|
||||||
const h: isize = @intCast(self.terminal_buffer.height);
|
|
||||||
line.length = @mod(randint, h - 3) + 3;
|
|
||||||
self.dots[x].value = @mod(randint, MAX_CODEPOINT) + 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 == -1)) {
|
|
||||||
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 != -1) {
|
|
||||||
dot.is_head = false;
|
|
||||||
if (MID_SCROLL_CHANGE) {
|
|
||||||
const randint = self.terminal_buffer.random.int(i16);
|
|
||||||
if (@mod(randint, 8) == 0) {
|
|
||||||
dot.value = @mod(randint, MAX_CODEPOINT) + 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(i16);
|
|
||||||
dot.value = @mod(randint, MAX_CODEPOINT) + 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 = -1;
|
|
||||||
}
|
|
||||||
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];
|
|
||||||
|
|
||||||
var fg = self.fg_ini;
|
|
||||||
|
|
||||||
if (dot.value == -1 or dot.value == ' ') {
|
|
||||||
_ = termbox.tb_set_cell(@intCast(x), @intCast(y - 1), ' ', fg, termbox.TB_DEFAULT);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dot.is_head) fg = @intCast(termbox.TB_WHITE | termbox.TB_BOLD);
|
|
||||||
_ = termbox.tb_set_cell(@intCast(x), @intCast(y - 1), @intCast(dot.value), fg, termbox.TB_DEFAULT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn initBuffers(dots: []Dot, lines: []Line, width: usize, height: usize, random: Random) void {
|
|
||||||
var y: usize = 0;
|
|
||||||
while (y <= height) : (y += 1) {
|
|
||||||
var x: usize = 0;
|
|
||||||
while (x < width) : (x += 2) {
|
|
||||||
dots[y * width + x].value = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var x: usize = 0;
|
|
||||||
while (x < width) : (x += 2) {
|
|
||||||
var line = lines[x];
|
|
||||||
const h: isize = @intCast(height);
|
|
||||||
line.space = @mod(random.int(i16), h) + 1;
|
|
||||||
line.length = @mod(random.int(i16), h - 3) + 3;
|
|
||||||
line.update = @mod(random.int(i16), 3) + 1;
|
|
||||||
lines[x] = line;
|
|
||||||
|
|
||||||
dots[width + x].value = ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
599
src/auth.zig
599
src/auth.zig
@@ -1,599 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const build_options = @import("build_options");
|
|
||||||
const builtin = @import("builtin");
|
|
||||||
const enums = @import("enums.zig");
|
|
||||||
const interop = @import("interop.zig");
|
|
||||||
const TerminalBuffer = @import("tui/TerminalBuffer.zig");
|
|
||||||
const Session = @import("tui/components/Session.zig");
|
|
||||||
const Text = @import("tui/components/Text.zig");
|
|
||||||
const Config = @import("config/Config.zig");
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const Md5 = std.crypto.hash.Md5;
|
|
||||||
const utmp = interop.utmp;
|
|
||||||
const Utmp = utmp.utmpx;
|
|
||||||
const SharedError = @import("SharedError.zig");
|
|
||||||
|
|
||||||
// When setting the currentLogin you must deallocate the previous currentLogin first
|
|
||||||
pub var currentLogin: ?[:0]const u8 = null;
|
|
||||||
pub var asyncPamHandle: ?*interop.pam.pam_handle = null;
|
|
||||||
pub var current_environment: ?Session.Environment = null;
|
|
||||||
|
|
||||||
fn environment_equals(e0: Session.Environment, e1: Session.Environment) bool {
|
|
||||||
if (!std.mem.eql(u8, e0.cmd, e1.cmd)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!std.mem.eql(u8, e0.name, e1.name)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!std.mem.eql(u8, e0.specifier, e1.specifier)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!(e0.xdg_desktop_names == null and e1.xdg_desktop_names == null) or
|
|
||||||
(e0.xdg_desktop_names != null and e1.xdg_desktop_names != null and !std.mem.eql(u8, e0.xdg_desktop_names.?, e1.xdg_desktop_names.?)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!(e0.xdg_session_desktop == null and e1.xdg_session_desktop == null) or
|
|
||||||
(e0.xdg_session_desktop != null and e1.xdg_session_desktop != null and !std.mem.eql(u8, e0.xdg_session_desktop.?, e1.xdg_session_desktop.?)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (e0.display_server != e1.display_server) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn automaticLogin(config: Config, login: [:0]const u8, environment: Session.Environment, wakesem: *std.Thread.Semaphore) !void {
|
|
||||||
while (asyncPamHandle == null and currentLogin != null and std.mem.eql(u8, currentLogin.?, login)) {
|
|
||||||
if (authenticate(config, login, "", environment)) |handle| {
|
|
||||||
if (currentLogin != null and !std.mem.eql(u8, currentLogin.?, login) and environment_equals(current_environment.?, environment)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
asyncPamHandle = handle;
|
|
||||||
wakesem.post();
|
|
||||||
return;
|
|
||||||
} else |_| {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn startAutomaticLogin(allocator: std.mem.Allocator, config: Config, login: Text, environment: Session.Environment, wakesem: *std.Thread.Semaphore) !void {
|
|
||||||
if (currentLogin) |clogin| {
|
|
||||||
allocator.free(clogin);
|
|
||||||
currentLogin = null;
|
|
||||||
}
|
|
||||||
const login_text = try allocator.dupeZ(u8, login.text.items);
|
|
||||||
currentLogin = login_text;
|
|
||||||
var handle = try std.Thread.spawn(.{}, automaticLogin, .{
|
|
||||||
config,
|
|
||||||
login_text,
|
|
||||||
environment,
|
|
||||||
wakesem,
|
|
||||||
});
|
|
||||||
handle.detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
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(
|
|
||||||
config: Config,
|
|
||||||
login: [:0]const u8,
|
|
||||||
password: [:0]const u8,
|
|
||||||
environment: Session.Environment,
|
|
||||||
) !*interop.pam.pam_handle {
|
|
||||||
var pam_tty_buffer: [6]u8 = undefined;
|
|
||||||
const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{config.tty});
|
|
||||||
var tty_buffer: [2]u8 = undefined;
|
|
||||||
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty});
|
|
||||||
|
|
||||||
// Set the XDG environment variables
|
|
||||||
setXdgSessionEnv(environment.display_server);
|
|
||||||
try setXdgEnv(tty_str, environment.xdg_session_desktop orelse "", environment.xdg_desktop_names orelse "");
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// Do the PAM routine
|
|
||||||
var status = interop.pam.pam_start(config.service_name, null, &conv, &handle);
|
|
||||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
|
||||||
errdefer _ = 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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
errdefer _ = interop.pam.pam_end(handle, status);
|
|
||||||
|
|
||||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
|
||||||
return handle.?;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn finaliseAuth(config: Config, environment: Session.Environment, handle: ?*interop.pam.pam_handle, login: [:0]const u8) !void {
|
|
||||||
var status: c_int = undefined;
|
|
||||||
defer status = interop.pam.pam_end(handle, status);
|
|
||||||
defer status = interop.pam.pam_setcred(handle, interop.pam.PAM_DELETE_CRED);
|
|
||||||
|
|
||||||
// Open the PAM session
|
|
||||||
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, status);
|
|
||||||
|
|
||||||
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(config, pwd, handle, 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,
|
|
||||||
};
|
|
||||||
try 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(
|
|
||||||
config: Config,
|
|
||||||
pwd: *interop.pwd.passwd,
|
|
||||||
handle: ?*interop.pam.pam_handle,
|
|
||||||
environment: Session.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, config.path);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// Execute what the user requested
|
|
||||||
switch (environment.display_server) {
|
|
||||||
.wayland => try executeWaylandCmd(pwd.pw_shell.?, config, environment.cmd),
|
|
||||||
.shell => try executeShellCmd(pwd.pw_shell.?, config),
|
|
||||||
.xinitrc, .x11 => if (build_options.enable_x11_support) {
|
|
||||||
var vt_buf: [5]u8 = undefined;
|
|
||||||
const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{config.tty});
|
|
||||||
try executeX11Cmd(pwd.pw_shell.?, pwd.pw_dir.?, config, 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, desktop_name: [:0]const u8, xdg_desktop_names: [:0]const u8) !void {
|
|
||||||
// The "/run/user/%d" directory is not available on FreeBSD. It is much
|
|
||||||
// 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: [10 + @sizeOf(u32) + 1]u8 = undefined;
|
|
||||||
const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid});
|
|
||||||
|
|
||||||
_ = interop.stdlib.setenv("XDG_RUNTIME_DIR", uid_str, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = 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);
|
|
||||||
_ = 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, config: Config) !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) {
|
|
||||||
const log_file = try redirectStandardStreams(config.session_log, true);
|
|
||||||
defer log_file.close();
|
|
||||||
|
|
||||||
var cmd_buffer: [1024]u8 = undefined;
|
|
||||||
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . {s}", .{ config.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, config: Config) !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}", .{ config.setup_cmd, config.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, config: Config, desktop_cmd: []const u8) !void {
|
|
||||||
const log_file = try redirectStandardStreams(config.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}", .{ config.setup_cmd, config.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, config: Config, 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, config);
|
|
||||||
|
|
||||||
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} >{s} 2>&1", .{ config.x_cmd, display_name, vt, config.session_log }) 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} >{s} 2>&1", .{ config.setup_cmd, config.login_cmd orelse "", desktop_cmd, config.session_log }) 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,
|
|
||||||
};
|
|
||||||
try 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, 0) catch return;
|
|
||||||
std.posix.kill(x_pid, std.posix.SIG.TERM) catch {};
|
|
||||||
|
|
||||||
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,58 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const interop = @import("interop.zig");
|
|
||||||
const utils = @import("tui/utils.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 termbox = interop.termbox;
|
|
||||||
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: u16, bg: u16, bigclock: Bigclock) [SIZE]utils.Cell {
|
|
||||||
var cells: [SIZE]utils.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] = utils.initCell(clock_chars[i], fg, bg);
|
|
||||||
|
|
||||||
return cells;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn alphaBlit(x: usize, y: usize, tb_width: usize, tb_height: usize, cells: [SIZE]utils.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];
|
|
||||||
if (cell.ch != 0) utils.putCell(x + xx, y + yy, cell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn toBigNumber(char: u8, bigclock: Bigclock) []const 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,61 +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;
|
|
||||||
|
|
||||||
animation: Animation = .none,
|
|
||||||
animation_timeout_sec: u12 = 0,
|
|
||||||
asterisk: ?u8 = '*',
|
|
||||||
auth_fails: u64 = 10,
|
|
||||||
bg: u16 = 0,
|
|
||||||
bigclock: Bigclock = .none,
|
|
||||||
blank_box: bool = true,
|
|
||||||
border_fg: u16 = 8,
|
|
||||||
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: u16 = 3,
|
|
||||||
console_dev: []const u8 = "/dev/console",
|
|
||||||
default_input: Input = .login,
|
|
||||||
error_bg: u16 = 0,
|
|
||||||
error_fg: u16 = 258,
|
|
||||||
fg: u16 = 8,
|
|
||||||
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 = "/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/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,61 +0,0 @@
|
|||||||
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_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_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",
|
|
||||||
normal: []const u8 = "normal",
|
|
||||||
no_x11_support: []const u8 = "x11 support disabled at compile-time",
|
|
||||||
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",
|
|
||||||
xinitrc: [:0]const u8 = "xinitrc",
|
|
||||||
x11: []const u8 = "x11",
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
user: ?[]const u8 = null,
|
|
||||||
session_index: ?usize = null,
|
|
||||||
@@ -1,144 +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");
|
|
||||||
|
|
||||||
var temporary_allocator = std.heap.page_allocator;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std.mem.eql(u8, field.key, "wayland_specifier") or
|
|
||||||
std.mem.eql(u8, field.key, "max_desktop_len") or
|
|
||||||
std.mem.eql(u8, field.key, "max_login_len") or
|
|
||||||
std.mem.eql(u8, field.key, "max_password_len") or
|
|
||||||
std.mem.eql(u8, field.key, "mcookie_cmd") or
|
|
||||||
std.mem.eql(u8, field.key, "term_reset_cmd") or
|
|
||||||
std.mem.eql(u8, field.key, "term_restore_cursor_cmd") or
|
|
||||||
std.mem.eql(u8, field.key, "x_cmd_setup") or
|
|
||||||
std.mem.eql(u8, field.key, "wayland_cmd"))
|
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
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,30 +0,0 @@
|
|||||||
pub const Animation = enum {
|
|
||||||
none,
|
|
||||||
doom,
|
|
||||||
matrix,
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
||||||
111
src/interop.zig
111
src/interop.zig
@@ -1,111 +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);
|
|
||||||
if (len < 0) return error.CannotGetFormattedTime;
|
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
889
src/main.zig
889
src/main.zig
@@ -1,889 +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 interop = @import("interop.zig");
|
|
||||||
const Doom = @import("animations/Doom.zig");
|
|
||||||
const Matrix = @import("animations/Matrix.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 utils = @import("tui/utils.zig");
|
|
||||||
|
|
||||||
const Ini = ini.Ini;
|
|
||||||
const termbox = interop.termbox;
|
|
||||||
const unistd = interop.unistd;
|
|
||||||
const temporary_allocator = std.heap.page_allocator;
|
|
||||||
|
|
||||||
var session_pid: std.posix.pid_t = -1;
|
|
||||||
pub 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 eventThreadMain(event: *?termbox.tb_event, event_error: *c_int, timeout: *c_int, wake: *std.Thread.Semaphore, cont: *std.Thread.Semaphore) void {
|
|
||||||
while (true) {
|
|
||||||
cont.wait();
|
|
||||||
var e: termbox.tb_event = undefined;
|
|
||||||
event_error.* = termbox.tb_peek_event(&e, timeout.*);
|
|
||||||
event.* = e;
|
|
||||||
wake.post();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.GeneralPurposeAllocator(.{}){};
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
|
|
||||||
// to be able to stop the 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;
|
|
||||||
|
|
||||||
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, comment_characters, migrator.configFieldHandler) 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, comment_characters, null) 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, comment_characters, null) 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, comment_characters, migrator.configFieldHandler) 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, comment_characters, null) catch Lang{};
|
|
||||||
|
|
||||||
if (config.load) {
|
|
||||||
var user_buf: [32]u8 = undefined;
|
|
||||||
save = save_ini.readFileToStruct(save_path, comment_characters, null) 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,
|
|
||||||
};
|
|
||||||
try std.posix.sigaction(std.posix.SIG.TERM, &act, null);
|
|
||||||
|
|
||||||
_ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_NORMAL);
|
|
||||||
_ = 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();
|
|
||||||
|
|
||||||
var buffer = TerminalBuffer.init(config, 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, lang);
|
|
||||||
defer session.deinit();
|
|
||||||
|
|
||||||
session.addEnvironment(.{ .Name = lang.shell }, null, .shell) catch {
|
|
||||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (build_options.enable_x11_support) {
|
|
||||||
if (config.xinitrc) |xinitrc| {
|
|
||||||
session.addEnvironment(.{ .Name = lang.xinitrc, .Exec = xinitrc }, null, .xinitrc) 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
try session.crawl(config.waylandsessions, .wayland);
|
|
||||||
if (build_options.enable_x11_support) try session.crawl(config.xsessions, .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(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 doom: Doom = undefined;
|
|
||||||
var matrix: Matrix = undefined;
|
|
||||||
|
|
||||||
switch (config.animation) {
|
|
||||||
.none => {},
|
|
||||||
.doom => doom = try Doom.init(allocator, &buffer),
|
|
||||||
.matrix => matrix = try Matrix.init(allocator, &buffer, config.cmatrix_fg),
|
|
||||||
}
|
|
||||||
defer {
|
|
||||||
switch (config.animation) {
|
|
||||||
.none => {},
|
|
||||||
.doom => doom.deinit(),
|
|
||||||
.matrix => matrix.deinit(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const animate = config.animation != .none;
|
|
||||||
const shutdown_key = try std.fmt.parseInt(u8, config.shutdown_key[1..], 10);
|
|
||||||
const shutdown_len = try utils.strWidth(lang.shutdown);
|
|
||||||
const restart_key = try std.fmt.parseInt(u8, config.restart_key[1..], 10);
|
|
||||||
const restart_len = try utils.strWidth(lang.restart);
|
|
||||||
const sleep_key = try std.fmt.parseInt(u8, config.sleep_key[1..], 10);
|
|
||||||
const sleep_len = try utils.strWidth(lang.sleep);
|
|
||||||
const brightness_down_key = try std.fmt.parseInt(u8, config.brightness_down_key[1..], 10);
|
|
||||||
const brightness_down_len = try utils.strWidth(lang.brightness_down);
|
|
||||||
const brightness_up_key = try std.fmt.parseInt(u8, config.brightness_up_key[1..], 10);
|
|
||||||
const brightness_up_len = try utils.strWidth(lang.brightness_up);
|
|
||||||
|
|
||||||
var event_timeout: c_int = std.math.maxInt(c_int);
|
|
||||||
var event_error: c_int = undefined;
|
|
||||||
var event: ?termbox.tb_event = null;
|
|
||||||
var wake: std.Thread.Semaphore = .{};
|
|
||||||
var doneEvent: std.Thread.Semaphore = .{};
|
|
||||||
var run = true;
|
|
||||||
var update = true;
|
|
||||||
var resolution_changed = false;
|
|
||||||
var auth_fails: u64 = 0;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
};
|
|
||||||
|
|
||||||
doneEvent.post();
|
|
||||||
const thandle = try std.Thread.spawn(.{}, eventThreadMain, .{ &event, &event_error, &event_timeout, &wake, &doneEvent });
|
|
||||||
|
|
||||||
thandle.detach();
|
|
||||||
|
|
||||||
{
|
|
||||||
const current_environment = session.label.list.items[session.label.current];
|
|
||||||
try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake);
|
|
||||||
}
|
|
||||||
defer allocator.free(auth.currentLogin.?);
|
|
||||||
while (run) {
|
|
||||||
// If there's no input or there's an animation, a resolution change needs to be checked
|
|
||||||
if (!update or config.animation != .none) {
|
|
||||||
if (!update) std.time.sleep(std.time.ns_per_ms * 100);
|
|
||||||
|
|
||||||
_ = termbox.tb_present(); // Required to update tb_width(), tb_height() and tb_cell_buffer()
|
|
||||||
|
|
||||||
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;
|
|
||||||
buffer.buffer = termbox.tb_cell_buffer();
|
|
||||||
|
|
||||||
switch (config.animation) {
|
|
||||||
.none => {},
|
|
||||||
.doom => doom.realloc() catch {
|
|
||||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
|
||||||
},
|
|
||||||
.matrix => matrix.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) {
|
|
||||||
switch (config.animation) {
|
|
||||||
.none => {},
|
|
||||||
.doom => doom.draw(),
|
|
||||||
.matrix => matrix.draw(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.bigclock != .none and buffer.box_height + (bigclock.HEIGHT + 2) * 2 < buffer.height) draw_big_clock: {
|
|
||||||
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) catch {
|
|
||||||
break :draw_big_clock;
|
|
||||||
};
|
|
||||||
|
|
||||||
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: {
|
|
||||||
var clock_buf: [32:0]u8 = undefined;
|
|
||||||
const clock_str = interop.timeAsString(&clock_buf, clock) catch {
|
|
||||||
break :draw_clock;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (clock_str.len == 0) return error.FormattedTimeEmpty;
|
|
||||||
|
|
||||||
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 = 0;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.drawLabel(config.brightness_down_key, length, 0);
|
|
||||||
length += config.brightness_down_key.len + 1;
|
|
||||||
buffer.drawLabel(" ", length - 1, 0);
|
|
||||||
|
|
||||||
buffer.drawLabel(lang.brightness_down, length, 0);
|
|
||||||
length += brightness_down_len + 1;
|
|
||||||
|
|
||||||
buffer.drawLabel(config.brightness_up_key, length, 0);
|
|
||||||
length += config.brightness_up_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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.time.sleep(std.time.ns_per_ms * 10);
|
|
||||||
update = buffer.cascade();
|
|
||||||
|
|
||||||
if (!update) {
|
|
||||||
std.time.sleep(std.time.ns_per_s * 7);
|
|
||||||
auth_fails = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = termbox.tb_present();
|
|
||||||
}
|
|
||||||
|
|
||||||
var timeout: u64 = std.math.maxInt(u64);
|
|
||||||
|
|
||||||
// Calculate the maximum timeout based on current animations, or the (big) clock. If there's none, we wait for 100ms 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;
|
|
||||||
switch (config.animation) {
|
|
||||||
.none => {},
|
|
||||||
.doom => doom.deinit(),
|
|
||||||
.matrix => matrix.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
var skipEvent: bool = false;
|
|
||||||
_ = wake.timedWait(timeout) catch .{};
|
|
||||||
if (auth.asyncPamHandle) |handle| {
|
|
||||||
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);
|
|
||||||
|
|
||||||
try info_line.addMessage(lang.authenticating, 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();
|
|
||||||
|
|
||||||
session_pid = try std.posix.fork();
|
|
||||||
if (session_pid == 0) {
|
|
||||||
const current_environment = session.label.list.items[session.label.current];
|
|
||||||
auth.finaliseAuth(config, current_environment, handle, login_text) catch |err| {
|
|
||||||
shared_err.writeError(err);
|
|
||||||
std.process.exit(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
std.process.exit(0);
|
|
||||||
}
|
|
||||||
_ = std.posix.waitpid(session_pid, 0);
|
|
||||||
|
|
||||||
session_pid = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auth.asyncPamHandle = null;
|
|
||||||
|
|
||||||
const auth_err = shared_err.readError();
|
|
||||||
if (auth_err) |err| {
|
|
||||||
try info_line.addMessage(getAuthErrorMsg(err, lang), config.error_bg, config.error_fg);
|
|
||||||
// We don't want to start login back in instantly. The user must first edit
|
|
||||||
// the login/desktop in order to login. Only in case of a failed login (so not a logout)
|
|
||||||
// should we automatically restart it.
|
|
||||||
const current_environment = session.label.list.items[session.label.current];
|
|
||||||
try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake);
|
|
||||||
} else {
|
|
||||||
try info_line.addMessage(lang.logout, config.error_bg, config.error_fg);
|
|
||||||
}
|
|
||||||
|
|
||||||
try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios);
|
|
||||||
_ = termbox.tb_clear();
|
|
||||||
_ = termbox.tb_present();
|
|
||||||
|
|
||||||
update = true;
|
|
||||||
|
|
||||||
_ = termbox.tb_set_cursor(0, 0);
|
|
||||||
_ = termbox.tb_present();
|
|
||||||
} else if (event) |e| {
|
|
||||||
defer doneEvent.post();
|
|
||||||
update = timeout != -1;
|
|
||||||
skipEvent = event_error < 0;
|
|
||||||
if (skipEvent or e.type != termbox.TB_EVENT_KEY) continue;
|
|
||||||
|
|
||||||
switch (e.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 - e.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.spawnAndWait() catch .{};
|
|
||||||
}
|
|
||||||
} else if (pressed_key == brightness_down_key) {
|
|
||||||
var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", config.brightness_down_cmd }, allocator);
|
|
||||||
_ = brightness.spawnAndWait() catch .{};
|
|
||||||
} else if (pressed_key == brightness_up_key) {
|
|
||||||
var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", config.brightness_up_cmd }, allocator);
|
|
||||||
_ = brightness.spawnAndWait() catch .{};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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 => {
|
|
||||||
if (active_input == .login) {
|
|
||||||
const current_environment = session.label.list.items[session.label.current];
|
|
||||||
try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake);
|
|
||||||
}
|
|
||||||
active_input = switch (active_input) {
|
|
||||||
.info_line => .session,
|
|
||||||
.session => .login,
|
|
||||||
.login, .password => .password,
|
|
||||||
};
|
|
||||||
update = true;
|
|
||||||
},
|
|
||||||
termbox.TB_KEY_TAB => {
|
|
||||||
if (active_input == .login) {
|
|
||||||
const current_environment = session.label.list.items[session.label.current];
|
|
||||||
try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake);
|
|
||||||
}
|
|
||||||
active_input = switch (active_input) {
|
|
||||||
.info_line => .session,
|
|
||||||
.session => .login,
|
|
||||||
.login => .password,
|
|
||||||
.password => .info_line,
|
|
||||||
};
|
|
||||||
update = true;
|
|
||||||
},
|
|
||||||
termbox.TB_KEY_BACK_TAB => {
|
|
||||||
if (active_input == .info_line) {
|
|
||||||
const current_environment = session.label.list.items[session.label.current];
|
|
||||||
try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake);
|
|
||||||
}
|
|
||||||
active_input = switch (active_input) {
|
|
||||||
.info_line => .password,
|
|
||||||
.session => .info_line,
|
|
||||||
.login => .session,
|
|
||||||
.password => .login,
|
|
||||||
};
|
|
||||||
|
|
||||||
update = true;
|
|
||||||
},
|
|
||||||
termbox.TB_KEY_ENTER => {
|
|
||||||
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, true, .{}) catch break :save_last_settings;
|
|
||||||
|
|
||||||
// Delete previous save file if it exists
|
|
||||||
if (migrator.maybe_save_file) |path| std.fs.cwd().deleteFile(path) catch {};
|
|
||||||
}
|
|
||||||
|
|
||||||
var shared_err = try SharedError.init();
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Give up control on the TTY
|
|
||||||
_ = termbox.tb_shutdown();
|
|
||||||
|
|
||||||
session_pid = try std.posix.fork();
|
|
||||||
const current_environment = session.label.list.items[session.label.current];
|
|
||||||
if (auth.authenticate(config, login_text, password_text, current_environment)) |handle| {
|
|
||||||
if (session_pid == 0) {
|
|
||||||
auth.finaliseAuth(config, current_environment, handle, login_text) catch |err| {
|
|
||||||
shared_err.writeError(err);
|
|
||||||
std.process.exit(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
std.process.exit(0);
|
|
||||||
}
|
|
||||||
_ = std.posix.waitpid(session_pid, 0);
|
|
||||||
} else |err| {
|
|
||||||
shared_err.writeError(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
session_pid = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take back control of the TTY
|
|
||||||
_ = termbox.tb_init();
|
|
||||||
_ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_NORMAL);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (e.ch) {
|
|
||||||
'k' => {
|
|
||||||
active_input = switch (active_input) {
|
|
||||||
.session, .info_line => .info_line,
|
|
||||||
.login => .session,
|
|
||||||
.password => .login,
|
|
||||||
};
|
|
||||||
update = true;
|
|
||||||
continue;
|
|
||||||
},
|
|
||||||
'j' => {
|
|
||||||
if (active_input == .login) {
|
|
||||||
const current_environment = session.label.list.items[session.label.current];
|
|
||||||
try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake);
|
|
||||||
}
|
|
||||||
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 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.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,199 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const builtin = @import("builtin");
|
|
||||||
const interop = @import("../interop.zig");
|
|
||||||
const utils = @import("utils.zig");
|
|
||||||
const Config = @import("../config/Config.zig");
|
|
||||||
|
|
||||||
const Random = std.Random;
|
|
||||||
|
|
||||||
const termbox = interop.termbox;
|
|
||||||
|
|
||||||
const TerminalBuffer = @This();
|
|
||||||
|
|
||||||
random: Random,
|
|
||||||
width: usize,
|
|
||||||
height: usize,
|
|
||||||
buffer: [*]termbox.tb_cell,
|
|
||||||
fg: u16,
|
|
||||||
bg: u16,
|
|
||||||
border_fg: u16,
|
|
||||||
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,
|
|
||||||
|
|
||||||
pub fn init(config: Config, labels_max_length: usize, random: Random) TerminalBuffer {
|
|
||||||
return .{
|
|
||||||
.random = random,
|
|
||||||
.width = @intCast(termbox.tb_width()),
|
|
||||||
.height = @intCast(termbox.tb_height()),
|
|
||||||
.buffer = termbox.tb_cell_buffer(),
|
|
||||||
.fg = config.fg,
|
|
||||||
.bg = config.bg,
|
|
||||||
.border_fg = config.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 * config.margin_box_h) + config.input_len + 1 + labels_max_length,
|
|
||||||
.box_height = 7 + (2 * config.margin_box_v),
|
|
||||||
.margin_box_v = config.margin_box_v,
|
|
||||||
.margin_box_h = config.margin_box_h,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cascade(self: TerminalBuffer) bool {
|
|
||||||
var changed = false;
|
|
||||||
var y = self.height - 2;
|
|
||||||
|
|
||||||
while (y > 0) : (y -= 1) {
|
|
||||||
for (0..self.width) |x| {
|
|
||||||
const cell = self.buffer[(y - 1) * self.width + x];
|
|
||||||
const cell_under = self.buffer[y * self.width + x];
|
|
||||||
|
|
||||||
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 = utils.initCell(self.box_chars.top, self.border_fg, self.bg);
|
|
||||||
var c2 = utils.initCell(self.box_chars.bottom, self.border_fg, self.bg);
|
|
||||||
|
|
||||||
for (0..self.box_width) |i| {
|
|
||||||
utils.putCell(x1 + i, y1 - 1, c1);
|
|
||||||
utils.putCell(x1 + i, y2, c2);
|
|
||||||
}
|
|
||||||
|
|
||||||
c1.ch = self.box_chars.left;
|
|
||||||
c2.ch = self.box_chars.right;
|
|
||||||
|
|
||||||
for (0..self.box_height) |i| {
|
|
||||||
utils.putCell(x1 - 1, y1 + i, c1);
|
|
||||||
utils.putCell(x2, y1 + i, c2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blank_box) {
|
|
||||||
const blank = utils.initCell(' ', self.fg, self.bg);
|
|
||||||
|
|
||||||
for (0..self.box_height) |y| {
|
|
||||||
for (0..self.box_width) |x| {
|
|
||||||
utils.putCell(x1 + x, y1 + y, blank);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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: u16, bg: u16) void {
|
|
||||||
const yc: c_int = @intCast(y);
|
|
||||||
const utf8view = std.unicode.Utf8View.init(text) catch return;
|
|
||||||
var utf8 = utf8view.iterator();
|
|
||||||
|
|
||||||
var i = x;
|
|
||||||
while (utf8.nextCodepoint()) |codepoint| : (i += 1) {
|
|
||||||
_ = termbox.tb_set_cell(@intCast(i), yc, codepoint, fg, bg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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: usize = 0;
|
|
||||||
while (utf8.nextCodepoint()) |codepoint| : (i += 1) {
|
|
||||||
if (i >= max_length) break;
|
|
||||||
_ = termbox.tb_set_cell(@intCast(i + x), yc, codepoint, self.fg, self.bg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drawCharMultiple(self: TerminalBuffer, char: u8, x: usize, y: usize, length: usize) void {
|
|
||||||
const cell = utils.initCell(char, self.fg, self.bg);
|
|
||||||
for (0..length) |xx| utils.putCell(x + xx, y, cell);
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
|
||||||
const generic = @import("generic.zig");
|
|
||||||
const utils = @import("../utils.zig");
|
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
|
|
||||||
const MessageLabel = generic.CyclableLabel(Message);
|
|
||||||
|
|
||||||
const InfoLine = @This();
|
|
||||||
|
|
||||||
const Message = struct {
|
|
||||||
width: u8,
|
|
||||||
text: []const u8,
|
|
||||||
bg: u16,
|
|
||||||
fg: u16,
|
|
||||||
};
|
|
||||||
|
|
||||||
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: u16, fg: u16) !void {
|
|
||||||
if (text.len == 0) return;
|
|
||||||
|
|
||||||
try self.label.addItem(.{
|
|
||||||
.width = try utils.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,142 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
|
||||||
const enums = @import("../../enums.zig");
|
|
||||||
const generic = @import("generic.zig");
|
|
||||||
const Ini = @import("zigini").Ini;
|
|
||||||
const Lang = @import("../../config/Lang.zig");
|
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
|
|
||||||
const DisplayServer = enums.DisplayServer;
|
|
||||||
|
|
||||||
const EnvironmentLabel = generic.CyclableLabel(Environment);
|
|
||||||
|
|
||||||
const Session = @This();
|
|
||||||
|
|
||||||
pub const Environment = struct {
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
const DesktopEntry = struct {
|
|
||||||
Exec: []const u8 = "",
|
|
||||||
Name: [:0]const u8 = "",
|
|
||||||
DesktopNames: ?[:0]u8 = null,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Entry = struct { @"Desktop Entry": DesktopEntry = .{} };
|
|
||||||
|
|
||||||
label: EnvironmentLabel,
|
|
||||||
lang: Lang,
|
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, lang: Lang) Session {
|
|
||||||
return .{
|
|
||||||
.label = EnvironmentLabel.init(allocator, buffer, drawItem),
|
|
||||||
.lang = lang,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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, entry: DesktopEntry, xdg_session_desktop: ?[:0]const u8, display_server: DisplayServer) !void {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
try self.label.addItem(.{
|
|
||||||
.entry_ini = null,
|
|
||||||
.name = entry.Name,
|
|
||||||
.xdg_session_desktop = xdg_session_desktop,
|
|
||||||
.xdg_desktop_names = xdg_desktop_names,
|
|
||||||
.cmd = entry.Exec,
|
|
||||||
.specifier = switch (display_server) {
|
|
||||||
.wayland => self.lang.wayland,
|
|
||||||
.x11 => self.lang.x11,
|
|
||||||
else => self.lang.other,
|
|
||||||
},
|
|
||||||
.display_server = display_server,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn addEnvironmentWithIni(self: *Session, entry_ini: Ini(Entry), xdg_session_desktop: ?[:0]const u8, display_server: DisplayServer) !void {
|
|
||||||
const entry = entry_ini.data.@"Desktop Entry";
|
|
||||||
var xdg_desktop_names: ?[:0]const u8 = null;
|
|
||||||
if (entry.DesktopNames) |desktop_names| {
|
|
||||||
for (desktop_names) |*c| {
|
|
||||||
if (c.* == ';') c.* = ':';
|
|
||||||
}
|
|
||||||
xdg_desktop_names = desktop_names;
|
|
||||||
}
|
|
||||||
|
|
||||||
try self.label.addItem(.{
|
|
||||||
.entry_ini = entry_ini,
|
|
||||||
.name = entry.Name,
|
|
||||||
.xdg_session_desktop = xdg_session_desktop,
|
|
||||||
.xdg_desktop_names = xdg_desktop_names,
|
|
||||||
.cmd = entry.Exec,
|
|
||||||
.specifier = switch (display_server) {
|
|
||||||
.wayland => self.lang.wayland,
|
|
||||||
.x11 => self.lang.x11,
|
|
||||||
else => self.lang.other,
|
|
||||||
},
|
|
||||||
.display_server = display_server,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn crawl(self: *Session, path: []const u8, display_server: DisplayServer) !void {
|
|
||||||
var iterable_directory = std.fs.openDirAbsolute(path, .{ .iterate = true }) catch return;
|
|
||||||
defer iterable_directory.close();
|
|
||||||
|
|
||||||
var iterator = iterable_directory.iterate();
|
|
||||||
while (try iterator.next()) |item| {
|
|
||||||
if (!std.mem.eql(u8, std.fs.path.extension(item.name), ".desktop")) continue;
|
|
||||||
|
|
||||||
const entry_path = try std.fmt.allocPrint(self.label.allocator, "{s}/{s}", .{ path, item.name });
|
|
||||||
defer self.label.allocator.free(entry_path);
|
|
||||||
var entry_ini = Ini(Entry).init(self.label.allocator);
|
|
||||||
_ = try entry_ini.readFileToStruct(entry_path, "#", null);
|
|
||||||
errdefer entry_ini.deinit();
|
|
||||||
|
|
||||||
var xdg_session_desktop: []const u8 = undefined;
|
|
||||||
const maybe_desktop_names = entry_ini.data.@"Desktop Entry".DesktopNames;
|
|
||||||
if (maybe_desktop_names) |desktop_names| {
|
|
||||||
xdg_session_desktop = std.mem.sliceTo(desktop_names, ';');
|
|
||||||
} else {
|
|
||||||
// if DesktopNames is empty, we'll take the name of the session file
|
|
||||||
xdg_session_desktop = std.fs.path.stem(item.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
const session_desktop = try self.label.allocator.dupeZ(u8, xdg_session_desktop);
|
|
||||||
errdefer self.label.allocator.free(session_desktop);
|
|
||||||
|
|
||||||
try self.addEnvironmentWithIni(entry_ini, session_desktop, display_server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,161 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const interop = @import("../../interop.zig");
|
|
||||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
|
||||||
const utils = @import("../utils.zig");
|
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const DynamicString = std.ArrayList(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: ?u8,
|
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, masked: bool, maybe_mask: ?u8) Text {
|
|
||||||
const text = DynamicString.init(allocator);
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
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.cursor, char);
|
|
||||||
|
|
||||||
self.end += 1;
|
|
||||||
self.goRight();
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const enums = @import("../../enums.zig");
|
|
||||||
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.ArrayList(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 = ItemList.init(allocator),
|
|
||||||
.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();
|
|
||||||
}
|
|
||||||
|
|
||||||
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(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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const interop = @import("../interop.zig");
|
|
||||||
|
|
||||||
const termbox = interop.termbox;
|
|
||||||
|
|
||||||
pub const Cell = struct {
|
|
||||||
ch: u32,
|
|
||||||
fg: u16,
|
|
||||||
bg: u16,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn initCell(ch: u32, fg: u16, bg: u16) Cell {
|
|
||||||
return .{
|
|
||||||
.ch = ch,
|
|
||||||
.fg = fg,
|
|
||||||
.bg = bg,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn putCell(x: usize, y: usize, cell: Cell) void {
|
|
||||||
_ = termbox.tb_set_cell(@intCast(x), @intCast(y), cell.ch, cell.fg, cell.bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Every codepoint is assumed to have a width of 1.
|
|
||||||
// Since ly should be running in a tty, this should be fine.
|
|
||||||
pub fn strWidth(str: []const u8) !u8 {
|
|
||||||
const utf8view = try std.unicode.Utf8View.init(str);
|
|
||||||
var utf8 = utf8view.iterator();
|
|
||||||
var i: u8 = 0;
|
|
||||||
while (utf8.nextCodepoint()) |_| i += 1;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
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