mirror of
https://github.com/fairyglade/ly.git
synced 2025-12-21 19:55:00 +00:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b9ea3d7cb | ||
|
|
83984dc493 | ||
|
|
a9449742d6 | ||
|
|
ea2dec50f5 | ||
|
|
fadbbf676a | ||
|
|
042aa50ff0 | ||
|
|
56202bc30e | ||
|
|
7300247e57 | ||
|
|
3ca2e8524b | ||
|
|
40d180da63 | ||
|
|
75cc971f9c | ||
|
|
9374d2df32 | ||
|
|
67fd024f6a | ||
|
|
5796720a9c | ||
|
|
802ad6bbed | ||
|
|
7ece95965b | ||
|
|
10cd9615ef | ||
|
|
a807e8e11c | ||
|
|
d87344330a | ||
|
|
a042749a72 | ||
|
|
391104cf34 | ||
|
|
8e534c7bcd | ||
|
|
6b7e7be387 | ||
|
|
53d252232f | ||
|
|
5cdd6af738 | ||
|
|
08f6ce5184 | ||
|
|
291e0d836b | ||
|
|
3d8d8d67df | ||
|
|
7506d6a7d5 | ||
|
|
a7615a33e0 | ||
|
|
0586f3424a | ||
|
|
b457b454ae | ||
|
|
d8d2d5a8bf | ||
|
|
4ee2b3ecc7 | ||
|
|
2ca870cfc5 | ||
|
|
42bf929756 | ||
|
|
0edb0012ab |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,3 +1,5 @@
|
|||||||
bin
|
.idea/
|
||||||
obj
|
zig-cache/
|
||||||
|
zig-out/
|
||||||
valgrind.log
|
valgrind.log
|
||||||
|
.zig-cache
|
||||||
|
|||||||
12
.gitmodules
vendored
12
.gitmodules
vendored
@@ -1,12 +0,0 @@
|
|||||||
[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/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
|
|
||||||
318
build.zig
Normal file
318
build.zig
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
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 = 0, .patch = 3 };
|
||||||
|
var dest_directory: []const u8 = undefined;
|
||||||
|
var data_directory: []const u8 = undefined;
|
||||||
|
var exe_name: []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 "";
|
||||||
|
data_directory = b.option([]const u8, "data_directory", "Specify a default data directory (default is /etc/ly). This path gets embedded into the binary") orelse "/etc/ly";
|
||||||
|
exe_name = b.option([]const u8, "name", "Specify installed executable file name (default is ly)") orelse "ly";
|
||||||
|
|
||||||
|
const bin_directory = try b.allocator.dupe(u8, data_directory);
|
||||||
|
data_directory = try std.fs.path.join(b.allocator, &[_][]const u8{ dest_directory, data_directory });
|
||||||
|
|
||||||
|
const build_options = b.addOptions();
|
||||||
|
build_options.addOption([]const u8, "data_directory", bin_directory);
|
||||||
|
|
||||||
|
const version_str = try getVersionStr(b, "ly", ly_version);
|
||||||
|
|
||||||
|
build_options.addOption([]const u8, "version", version_str);
|
||||||
|
|
||||||
|
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");
|
||||||
|
exe.linkSystemLibrary("xcb");
|
||||||
|
exe.linkLibC();
|
||||||
|
|
||||||
|
// HACK: Only fails with ReleaseSafe, so we'll override it.
|
||||||
|
const translate_c = b.addTranslateC(.{
|
||||||
|
.root_source_file = b.path("include/termbox2.h"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = if (optimize == .ReleaseSafe) .ReleaseFast else optimize,
|
||||||
|
});
|
||||||
|
translate_c.defineCMacroRaw("TB_IMPL");
|
||||||
|
const termbox2 = translate_c.addModule("termbox2");
|
||||||
|
exe.root_module.addImport("termbox2", termbox2);
|
||||||
|
|
||||||
|
if (optimize == .ReleaseSafe) {
|
||||||
|
std.debug.print("warn: termbox2 module is being built in ReleaseFast due to a bug.\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
b.installArtifact(exe);
|
||||||
|
|
||||||
|
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 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, progress: ProgressNode) !void {
|
||||||
|
_ = progress;
|
||||||
|
try install_ly(step.owner.allocator, install_conf);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const InitSystem = enum {
|
||||||
|
Systemd,
|
||||||
|
Openrc,
|
||||||
|
Runit,
|
||||||
|
};
|
||||||
|
pub fn ServiceInstaller(comptime init_system: InitSystem) type {
|
||||||
|
return struct {
|
||||||
|
pub fn make(step: *std.Build.Step, progress: ProgressNode) !void {
|
||||||
|
_ = progress;
|
||||||
|
const allocator = step.owner.allocator;
|
||||||
|
switch (init_system) {
|
||||||
|
.Openrc => {
|
||||||
|
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/etc/init.d" });
|
||||||
|
std.fs.cwd().makePath(service_path) catch {};
|
||||||
|
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
|
||||||
|
defer service_dir.close();
|
||||||
|
|
||||||
|
try std.fs.cwd().copyFile("res/ly-openrc", service_dir, exe_name, .{ .override_mode = 0o755 });
|
||||||
|
},
|
||||||
|
.Runit => {
|
||||||
|
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/etc/sv/ly" });
|
||||||
|
std.fs.cwd().makePath(service_path) catch {};
|
||||||
|
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
|
||||||
|
defer service_dir.close();
|
||||||
|
|
||||||
|
const supervise_path = try std.fs.path.join(allocator, &[_][]const u8{ service_path, "supervise" });
|
||||||
|
|
||||||
|
try std.fs.cwd().copyFile("res/ly-runit-service/conf", service_dir, "conf", .{});
|
||||||
|
try std.fs.cwd().copyFile("res/ly-runit-service/finish", service_dir, "finish", .{ .override_mode = 0o755 });
|
||||||
|
try std.fs.cwd().copyFile("res/ly-runit-service/run", service_dir, "run", .{ .override_mode = 0o755 });
|
||||||
|
try std.fs.cwd().symLink("/run/runit/supervise.ly", supervise_path, .{});
|
||||||
|
},
|
||||||
|
.Systemd => {
|
||||||
|
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/usr/lib/systemd/system" });
|
||||||
|
std.fs.cwd().makePath(service_path) catch {};
|
||||||
|
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
|
||||||
|
defer service_dir.close();
|
||||||
|
|
||||||
|
try std.fs.cwd().copyFile("res/ly.service", service_dir, "ly.service", .{ .override_mode = 0o644 });
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_ly(allocator: std.mem.Allocator, install_config: bool) !void {
|
||||||
|
std.fs.cwd().makePath(data_directory) catch {
|
||||||
|
std.debug.print("warn: {s} already exists as a directory.\n", .{data_directory});
|
||||||
|
};
|
||||||
|
|
||||||
|
const lang_path = try std.fs.path.join(allocator, &[_][]const u8{ data_directory, "/lang" });
|
||||||
|
std.fs.cwd().makePath(lang_path) catch {
|
||||||
|
std.debug.print("warn: {s} already exists as a directory.\n", .{data_directory});
|
||||||
|
};
|
||||||
|
|
||||||
|
var current_dir = std.fs.cwd();
|
||||||
|
|
||||||
|
{
|
||||||
|
const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/usr/bin" });
|
||||||
|
std.fs.cwd().makePath(exe_path) catch {
|
||||||
|
if (!std.mem.eql(u8, dest_directory, "")) {
|
||||||
|
std.debug.print("warn: {s} already exists as a directory.\n", .{exe_path});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var executable_dir = std.fs.cwd().openDir(exe_path, .{}) catch unreachable;
|
||||||
|
defer executable_dir.close();
|
||||||
|
|
||||||
|
try current_dir.copyFile("zig-out/bin/ly", executable_dir, exe_name, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var config_dir = std.fs.cwd().openDir(data_directory, .{}) catch unreachable;
|
||||||
|
defer config_dir.close();
|
||||||
|
|
||||||
|
if (install_config) {
|
||||||
|
try current_dir.copyFile("res/config.ini", config_dir, "config.ini", .{});
|
||||||
|
}
|
||||||
|
try current_dir.copyFile("res/xsetup.sh", config_dir, "xsetup.sh", .{});
|
||||||
|
try current_dir.copyFile("res/wsetup.sh", config_dir, "wsetup.sh", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var lang_dir = std.fs.cwd().openDir(lang_path, .{}) catch unreachable;
|
||||||
|
defer lang_dir.close();
|
||||||
|
|
||||||
|
try current_dir.copyFile("res/lang/cat.ini", lang_dir, "cat.ini", .{});
|
||||||
|
try current_dir.copyFile("res/lang/cs.ini", lang_dir, "cs.ini", .{});
|
||||||
|
try current_dir.copyFile("res/lang/de.ini", lang_dir, "de.ini", .{});
|
||||||
|
try current_dir.copyFile("res/lang/en.ini", lang_dir, "en.ini", .{});
|
||||||
|
try current_dir.copyFile("res/lang/es.ini", lang_dir, "es.ini", .{});
|
||||||
|
try current_dir.copyFile("res/lang/fr.ini", lang_dir, "fr.ini", .{});
|
||||||
|
try current_dir.copyFile("res/lang/it.ini", lang_dir, "it.ini", .{});
|
||||||
|
try current_dir.copyFile("res/lang/pl.ini", lang_dir, "pl.ini", .{});
|
||||||
|
try current_dir.copyFile("res/lang/pt.ini", lang_dir, "pt.ini", .{});
|
||||||
|
try current_dir.copyFile("res/lang/pt_BR.ini", lang_dir, "pt_BR.ini", .{});
|
||||||
|
try current_dir.copyFile("res/lang/ro.ini", lang_dir, "ro.ini", .{});
|
||||||
|
try current_dir.copyFile("res/lang/ru.ini", lang_dir, "ru.ini", .{});
|
||||||
|
try current_dir.copyFile("res/lang/sr.ini", lang_dir, "sr.ini", .{});
|
||||||
|
try current_dir.copyFile("res/lang/sv.ini", lang_dir, "sv.ini", .{});
|
||||||
|
try current_dir.copyFile("res/lang/tr.ini", lang_dir, "tr.ini", .{});
|
||||||
|
try current_dir.copyFile("res/lang/uk.ini", lang_dir, "uk.ini", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const pam_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/etc/pam.d" });
|
||||||
|
|
||||||
|
std.fs.cwd().makePath(pam_path) catch {
|
||||||
|
if (!std.mem.eql(u8, dest_directory, "")) {
|
||||||
|
std.debug.print("warn: {s} already exists as a directory.\n", .{pam_path});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var pam_dir = std.fs.cwd().openDir(pam_path, .{}) catch unreachable;
|
||||||
|
defer pam_dir.close();
|
||||||
|
|
||||||
|
try current_dir.copyFile("res/pam.d/ly", pam_dir, "ly", .{ .override_mode = 0o644 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uninstallall(step: *std.Build.Step, progress: ProgressNode) !void {
|
||||||
|
_ = progress;
|
||||||
|
try std.fs.cwd().deleteTree(data_directory);
|
||||||
|
const allocator = step.owner.allocator;
|
||||||
|
|
||||||
|
const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/usr/bin/", exe_name });
|
||||||
|
try std.fs.cwd().deleteFile(exe_path);
|
||||||
|
|
||||||
|
const pam_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/etc/pam.d/ly" });
|
||||||
|
try std.fs.cwd().deleteFile(pam_path);
|
||||||
|
|
||||||
|
const systemd_service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/usr/lib/systemd/system/ly.service" });
|
||||||
|
std.fs.cwd().deleteFile(systemd_service_path) catch {
|
||||||
|
std.debug.print("warn: systemd service not found.\n", .{});
|
||||||
|
};
|
||||||
|
|
||||||
|
const openrc_service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/etc/init.d/ly" });
|
||||||
|
std.fs.cwd().deleteFile(openrc_service_path) catch {
|
||||||
|
std.debug.print("warn: openrc service not found.\n", .{});
|
||||||
|
};
|
||||||
|
|
||||||
|
const runit_service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, "/etc/sv/ly" });
|
||||||
|
std.fs.cwd().deleteTree(runit_service_path) catch {
|
||||||
|
std.debug.print("warn: runit service not found.\n", .{});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
16
build.zig.zon
Normal file
16
build.zig.zon
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
.{
|
||||||
|
.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 = .{""},
|
||||||
|
}
|
||||||
52
changelog.md
Normal file
52
changelog.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Zig Rewrite (Version 1.0.0)
|
||||||
|
|
||||||
|
## Config Options
|
||||||
|
|
||||||
|
res/config.ini contains all of the available config options and their default values.
|
||||||
|
|
||||||
|
### Additions
|
||||||
|
|
||||||
|
+ `border_fg` has been introduced to change the color of the borders.
|
||||||
|
+ `term_restore_cursor_cmd` should restore the cursor to it's usual state.
|
||||||
|
+ `vi_mode` to enable vi keybindings.
|
||||||
|
+ `sleep_key` and `sleep_cmd`.
|
||||||
|
+ `numlock` to set numlock on startup.
|
||||||
|
+ `initial_info_text` allows changing the initial text on the info line.
|
||||||
|
+ `box_title` to display a title at the top of the main box
|
||||||
|
|
||||||
|
Note: `sleep_cmd` is unset by default, meaning it's hidden and has no effect.
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
+ xinitrc can be set to null to hide it.
|
||||||
|
+ `blank_password` has been renamed to `clear_password`.
|
||||||
|
+ `save_file` has been deprecated and will be removed in a future version.
|
||||||
|
|
||||||
|
### Removals
|
||||||
|
|
||||||
|
+ `wayland_specifier` has been removed.
|
||||||
|
|
||||||
|
## Save File
|
||||||
|
|
||||||
|
The save file is now in .ini format and stored in the same directory as the config.
|
||||||
|
Older save files will be migrated to the new format.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
user = ash
|
||||||
|
session_index = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Misc
|
||||||
|
|
||||||
|
+ Display server name added next to selected session.
|
||||||
|
+ getty@tty2 has been added as a conflict in res/ly.service, so if it is running, ly should still be able to start.
|
||||||
|
+ `XDG_CURRENT_DESKTOP` is now set by ly.
|
||||||
|
+ LANG is no longer set by ly.
|
||||||
|
+ X Server PID is fetched from /tmp/X{d}.lock to be able to kill the process since it detaches.
|
||||||
|
+ Non .desktop files are now ignored in sessions directory.
|
||||||
|
+ PAM auth is now done in a child process. (Fixes some issues with logging out and back in).
|
||||||
|
+ When ly receives SIGTERM, the terminal is now cleared and existing child processes are cleaned up.
|
||||||
|
+ Shift+Tab now focuses previous input.
|
||||||
|
+ Display text in the info line when authenticating.
|
||||||
3448
include/termbox2.h
Normal file
3448
include/termbox2.h
Normal file
File diff suppressed because it is too large
Load Diff
118
makefile
118
makefile
@@ -1,118 +0,0 @@
|
|||||||
NAME = ly
|
|
||||||
CC = gcc
|
|
||||||
FLAGS = -std=c99 -pedantic -g
|
|
||||||
FLAGS+= -Wall -Wextra -Werror=vla -Wno-unused-parameter
|
|
||||||
#FLAGS+= -DDEBUG
|
|
||||||
FLAGS+= -DLY_VERSION=\"$(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 ly"
|
|
||||||
@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)/pam.d/ly -m 644 -t ${DESTDIR}/etc/pam.d
|
|
||||||
|
|
||||||
installnoconf: $(BIND)/$(NAME)
|
|
||||||
@echo "installing ly 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)/pam.d/ly -m 644 -t ${DESTDIR}/etc/pam.d
|
|
||||||
|
|
||||||
installsystemd:
|
|
||||||
@echo "installing systemd service"
|
|
||||||
@install -DZ $(RESD)/ly.service -m 644 -t ${DESTDIR}/usr/lib/systemd/system
|
|
||||||
|
|
||||||
installopenrc:
|
|
||||||
@echo "installing openrc service"
|
|
||||||
@install -DZ $(RESD)/ly-openrc -m 755 -T ${DESTDIR}/etc/init.d/${NAME}
|
|
||||||
|
|
||||||
installrunit:
|
|
||||||
@echo "installing runit service"
|
|
||||||
@install -DZ $(RESD)/ly-runit-service/* -t ${DESTDIR}/etc/sv/ly
|
|
||||||
|
|
||||||
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
|
|
||||||
@rm -f ${DESTDIR}/etc/init.d/${NAME}
|
|
||||||
@rm -rf ${DESTDIR}/etc/sv/ly
|
|
||||||
|
|
||||||
clean:
|
|
||||||
@echo "cleaning"
|
|
||||||
@rm -rf $(BIND) $(OBJD) valgrind.log
|
|
||||||
@(cd $(SUBD)/termbox_next && $(MAKE) clean)
|
|
||||||
80
readme.md
80
readme.md
@@ -5,28 +5,39 @@
|
|||||||
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
|
||||||
- a C99 compiler (tested with tcc and gcc)
|
- Compile-time:
|
||||||
- a C standard library
|
- zig 0.12.0 or 0.13.0
|
||||||
- GNU make
|
- a C standard library
|
||||||
- pam
|
- pam
|
||||||
- xcb
|
- xcb
|
||||||
- xorg
|
- Runtime (with default config):
|
||||||
- xorg-xauth
|
- xorg
|
||||||
- mcookie
|
- xorg-xauth
|
||||||
- tput
|
- mcookie
|
||||||
- shutdown
|
- tput
|
||||||
|
- shutdown
|
||||||
|
|
||||||
On Debian-based distros running `apt install build-essential libpam0g-dev libxcb-xkb-dev` as root should install all the dependencies for you.
|
### Debian
|
||||||
For Fedora try running `dnf install make automake gcc gcc-c++ kernel-devel pam-devel libxcb-devel`
|
```
|
||||||
|
# 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
|
- awesome
|
||||||
- bspwm
|
- bspwm
|
||||||
- budgie
|
- budgie
|
||||||
- cinnamon
|
- cinnamon
|
||||||
- deepin
|
- deepin
|
||||||
|
- dwl
|
||||||
- dwm
|
- dwm
|
||||||
- enlightenment
|
- enlightenment
|
||||||
- gnome
|
- gnome
|
||||||
@@ -57,7 +68,7 @@ changing the source code won't be necessary :)
|
|||||||
## Cloning and Compiling
|
## Cloning and Compiling
|
||||||
Clone the repository
|
Clone the repository
|
||||||
```
|
```
|
||||||
$ git clone --recurse-submodules https://github.com/fairyglade/ly
|
$ git clone https://github.com/fairyglade/ly
|
||||||
```
|
```
|
||||||
|
|
||||||
Change the directory to ly
|
Change the directory to ly
|
||||||
@@ -67,18 +78,18 @@ $ cd ly
|
|||||||
|
|
||||||
Compile
|
Compile
|
||||||
```
|
```
|
||||||
$ make
|
$ zig build
|
||||||
```
|
```
|
||||||
|
|
||||||
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)
|
||||||
```
|
```
|
||||||
# make run
|
# zig build run
|
||||||
```
|
```
|
||||||
|
|
||||||
Install Ly and the provided systemd service file
|
Install Ly and the provided systemd service file
|
||||||
```
|
```
|
||||||
# make install installsystemd
|
# zig build installsystemd
|
||||||
```
|
```
|
||||||
|
|
||||||
Enable the service
|
Enable the service
|
||||||
@@ -93,12 +104,13 @@ disable getty on Ly's tty to prevent "login" from spawning on top of it
|
|||||||
```
|
```
|
||||||
|
|
||||||
### OpenRC
|
### OpenRC
|
||||||
|
**NOTE**: On Gentoo, Ly will disable the `display-manager-init` service in order to run.
|
||||||
|
|
||||||
Clone, compile and test.
|
Clone, compile and test.
|
||||||
|
|
||||||
Install Ly and the provided OpenRC service
|
Install Ly and the provided OpenRC service
|
||||||
```
|
```
|
||||||
# make install installopenrc
|
# zig build installopenrc
|
||||||
```
|
```
|
||||||
|
|
||||||
Enable the service
|
Enable the service
|
||||||
@@ -108,16 +120,15 @@ Enable the service
|
|||||||
|
|
||||||
You can edit which tty Ly will start on by editing the `tty` option in the configuration file.
|
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 the getty so it doesn't respawn on top of ly
|
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
|
# rc-update del agetty.tty2
|
||||||
```
|
```
|
||||||
|
|
||||||
### runit
|
### runit
|
||||||
|
|
||||||
```
|
```
|
||||||
$ make
|
# zig build installrunit
|
||||||
# make install installrunit
|
|
||||||
# ln -s /etc/sv/ly /var/service/
|
# ln -s /etc/sv/ly /var/service/
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -129,12 +140,28 @@ You should as well disable your existing display manager service if needed, e.g.
|
|||||||
# rm /var/service/lxdm
|
# 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:
|
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
|
# rm /var/service/agetty-tty2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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
|
## Arch Linux Installation
|
||||||
You can install ly from the [`[extra]` repos](https://archlinux.org/packages/extra/x86_64/ly/):
|
You can install ly from the [`[extra]` repos](https://archlinux.org/packages/extra/x86_64/ly/):
|
||||||
```
|
```
|
||||||
@@ -169,9 +196,12 @@ 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 uncomment `animate = true` in `/etc/ly/config.ini`. You may also
|
just set `animation = doom` 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.
|
||||||
|
|
||||||
|
## Gentoo (OpenRC) installation tip
|
||||||
|
To avoid a console spawning on top on Ly, comment out the appropriate line from /etc/inittab (default is 2).
|
||||||
|
|||||||
140
res/config.ini
140
res/config.ini
@@ -1,24 +1,25 @@
|
|||||||
# Animation enabled/disabled
|
|
||||||
#animate = false
|
|
||||||
|
|
||||||
# The active animation
|
# The active animation
|
||||||
# 0 -> PSX DOOM fire (default)
|
# none -> Nothing (default)
|
||||||
# 1 -> CMatrix
|
# doom -> PSX DOOM fire
|
||||||
#animation = 0
|
# matrix -> CMatrix
|
||||||
|
animation = none
|
||||||
|
|
||||||
# format string for clock in top right corner (see strftime specification)
|
# Format string for clock in top right corner (see strftime specification). Example: %c
|
||||||
#clock = %c
|
clock = null
|
||||||
|
|
||||||
# enable/disable big clock
|
# Enable/disable big clock
|
||||||
#bigclock = true
|
bigclock = false
|
||||||
|
|
||||||
# The character used to mask the password
|
# The character used to mask the password
|
||||||
#asterisk = *
|
asterisk = *
|
||||||
|
|
||||||
# Erase password input on failure
|
# Erase password input on failure
|
||||||
#blank_password = false
|
clear_password = false
|
||||||
|
|
||||||
#The `fg` and `bg` color settings take a digit 0-8 corresponding to:
|
# Enable vi keybindings
|
||||||
|
vi_mode = false
|
||||||
|
|
||||||
|
# The `fg` and `bg` color settings take a digit 0-8 corresponding to:
|
||||||
#define TB_DEFAULT 0x00
|
#define TB_DEFAULT 0x00
|
||||||
#define TB_BLACK 0x01
|
#define TB_BLACK 0x01
|
||||||
#define TB_RED 0x02
|
#define TB_RED 0x02
|
||||||
@@ -38,103 +39,124 @@
|
|||||||
# config) will be used by `ly` for `fg = 8`.
|
# config) will be used by `ly` for `fg = 8`.
|
||||||
|
|
||||||
# Background color id
|
# Background color id
|
||||||
#bg = 0
|
bg = 0
|
||||||
|
|
||||||
# Foreground color id
|
# Foreground color id
|
||||||
#fg = 9
|
fg = 8
|
||||||
|
|
||||||
|
# Border color
|
||||||
|
border_fg = 8
|
||||||
|
|
||||||
|
# Title to show at the top of the main box
|
||||||
|
box_title = null
|
||||||
|
|
||||||
|
# Initial text to show on the info line (Defaults to hostname)
|
||||||
|
initial_info_text = null
|
||||||
|
|
||||||
# Blank main box background
|
# Blank main box background
|
||||||
# Setting to false will make it transparent
|
# Setting to false will make it transparent
|
||||||
#blank_box = true
|
blank_box = true
|
||||||
|
|
||||||
# Remove main box borders
|
# Remove main box borders
|
||||||
#hide_borders = false
|
hide_borders = false
|
||||||
|
|
||||||
# Main box margins
|
# Main box margins
|
||||||
#margin_box_h = 2
|
margin_box_h = 2
|
||||||
#margin_box_v = 1
|
margin_box_v = 1
|
||||||
|
|
||||||
# Input boxes length
|
# Input boxes length
|
||||||
#input_len = 34
|
input_len = 34
|
||||||
|
|
||||||
# Max input sizes
|
# Max input sizes
|
||||||
#max_desktop_len = 100
|
max_desktop_len = 100
|
||||||
#max_login_len = 255
|
max_login_len = 255
|
||||||
#max_password_len = 255
|
max_password_len = 255
|
||||||
|
|
||||||
|
|
||||||
# Input box active by default on startup
|
# Input box active by default on startup
|
||||||
#default_input = 2
|
# Available inputs: session, login, password
|
||||||
|
default_input = login
|
||||||
|
|
||||||
# Load the saved desktop and username
|
# Load the saved desktop and username
|
||||||
#load = true
|
load = true
|
||||||
|
|
||||||
# Save the current desktop and login as defaults
|
# Save the current desktop and login as defaults
|
||||||
#save = true
|
save = true
|
||||||
|
|
||||||
|
# Deprecated - Will be removed in a future version
|
||||||
|
# New save files are now loaded from the same directory as the config
|
||||||
|
# Currently used to migrate old save files to the new version
|
||||||
# File in which to save and load the default desktop and login
|
# File in which to save and load the default desktop and login
|
||||||
#save_file = /etc/ly/save
|
save_file = /etc/ly/save
|
||||||
|
|
||||||
|
# Remove power management command hints
|
||||||
|
hide_key_hints = false
|
||||||
|
|
||||||
# Remove F1/F2 command hints
|
# Specifies the key used for shutdown (F1-F12)
|
||||||
#hide_f1_commands = false
|
shutdown_key = F1
|
||||||
|
|
||||||
# Command executed when pressing F1
|
# Specifies the key used for restart (F1-F12)
|
||||||
#shutdown_cmd = /sbin/shutdown -a now
|
restart_key = F2
|
||||||
|
|
||||||
# Command executed when pressing F2
|
# Specifies the key used for sleep (F1-F12)
|
||||||
#restart_cmd = /sbin/shutdown -r now
|
sleep_key = F3
|
||||||
|
|
||||||
|
# Command executed when pressing shutdown_key
|
||||||
|
shutdown_cmd = /sbin/shutdown -a now
|
||||||
|
|
||||||
|
# Command executed when pressing restart_key
|
||||||
|
restart_cmd = /sbin/shutdown -r now
|
||||||
|
|
||||||
|
# Command executed when pressing sleep key (can be null)
|
||||||
|
sleep_cmd = null
|
||||||
|
|
||||||
# Active language
|
# Active language
|
||||||
# Available languages are found in /etc/ly/lang/
|
# Available languages are found in /etc/ly/lang/
|
||||||
#lang = en
|
lang = en
|
||||||
|
|
||||||
|
# TTY in use
|
||||||
# tty in use
|
tty = 2
|
||||||
#tty = 2
|
|
||||||
|
|
||||||
# Console path
|
# Console path
|
||||||
#console_dev = /dev/console
|
console_dev = /dev/console
|
||||||
|
|
||||||
# Default path
|
|
||||||
#path = /sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin
|
|
||||||
|
|
||||||
|
# Default path. If null, ly doesn't set a path.
|
||||||
|
path = /sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
|
||||||
|
|
||||||
# Event timeout in milliseconds
|
# Event timeout in milliseconds
|
||||||
#min_refresh_delta = 5
|
min_refresh_delta = 5
|
||||||
|
|
||||||
|
# Set numlock on/off at startup
|
||||||
|
numlock = false
|
||||||
|
|
||||||
# Service name (set to ly to use the provided pam config file)
|
# Service name (set to ly to use the provided pam config file)
|
||||||
#service_name = ly
|
service_name = ly
|
||||||
|
|
||||||
# Terminal reset command (tput is faster)
|
# Terminal reset command (tput is faster)
|
||||||
#term_reset_cmd = /usr/bin/tput reset
|
term_reset_cmd = /usr/bin/tput reset
|
||||||
|
|
||||||
|
# Terminal restore cursor command
|
||||||
|
term_restore_cursor_cmd = /usr/bin/tput cnorm
|
||||||
|
|
||||||
# Cookie generator
|
# Cookie generator
|
||||||
#mcookie_cmd = /usr/bin/mcookie
|
mcookie_cmd = /usr/bin/mcookie
|
||||||
|
|
||||||
|
|
||||||
# Wayland setup command
|
# Wayland setup command
|
||||||
#wayland_cmd = /etc/ly/wsetup.sh
|
wayland_cmd = /etc/ly/wsetup.sh
|
||||||
|
|
||||||
# Add wayland specifier to session names
|
|
||||||
#wayland_specifier = false
|
|
||||||
|
|
||||||
# Wayland desktop environments
|
# Wayland desktop environments
|
||||||
#waylandsessions = /usr/share/wayland-sessions
|
waylandsessions = /usr/share/wayland-sessions
|
||||||
|
|
||||||
|
# xinitrc (hidden if null)
|
||||||
# xinitrc
|
xinitrc = ~/.xinitrc
|
||||||
#xinitrc = ~/.xinitrc
|
|
||||||
|
|
||||||
# Xorg server command
|
# Xorg server command
|
||||||
#x_cmd = /usr/bin/X
|
x_cmd = /usr/bin/X
|
||||||
|
|
||||||
# Xorg setup command
|
# Xorg setup command
|
||||||
#x_cmd_setup = /etc/ly/xsetup.sh
|
x_cmd_setup = /etc/ly/xsetup.sh
|
||||||
|
|
||||||
# Xorg xauthority edition tool
|
# Xorg xauthority edition tool
|
||||||
#xauth_cmd = /usr/bin/xauth
|
xauth_cmd = /usr/bin/xauth
|
||||||
|
|
||||||
# Xorg desktop environments
|
# Xorg desktop environments
|
||||||
#xsessions = /usr/share/xsessions
|
xsessions = /usr/share/xsessions
|
||||||
|
|||||||
4
res/lang/cat.ini
Executable file → Normal file
4
res/lang/cat.ini
Executable file → Normal file
@@ -34,12 +34,12 @@ err_user_init = error al inicialitzar usuari
|
|||||||
err_user_uid = error al establir el UID de l'usuari
|
err_user_uid = error al establir el UID de l'usuari
|
||||||
err_xsessions_dir = error al cercar la carpeta de sessions
|
err_xsessions_dir = error al cercar la carpeta de sessions
|
||||||
err_xsessions_open = error al obrir la carpeta de sessions
|
err_xsessions_open = error al obrir la carpeta de sessions
|
||||||
f1 = F1 aturar
|
|
||||||
f2 = F2 reiniciar
|
|
||||||
login = iniciar sessió
|
login = iniciar sessió
|
||||||
logout = tancar sessió
|
logout = tancar sessió
|
||||||
numlock = Bloq Num
|
numlock = Bloq Num
|
||||||
password = Clau
|
password = Clau
|
||||||
|
restart = reiniciar
|
||||||
shell = shell
|
shell = shell
|
||||||
|
shutdown = aturar
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ capslock = capslock
|
|||||||
err_alloc = alokace paměti selhala
|
err_alloc = alokace paměti selhala
|
||||||
err_bounds = index je mimo hranice pole
|
err_bounds = index je mimo hranice pole
|
||||||
err_chdir = nelze otevřít domovský adresář
|
err_chdir = nelze otevřít domovský adresář
|
||||||
err_console_dev = chyba při přístupi do konzole
|
err_console_dev = chyba při přístupu do konzole
|
||||||
err_dgn_oob = zpráva protokolu
|
err_dgn_oob = zpráva protokolu
|
||||||
err_domain = neplatná doména
|
err_domain = neplatná doména
|
||||||
err_hostname = nelze získat název hostitele
|
err_hostname = nelze získat název hostitele
|
||||||
@@ -34,12 +34,12 @@ err_user_init = inicializace uživatele selhala
|
|||||||
err_user_uid = nastavení UID uživateli selhalo
|
err_user_uid = nastavení UID uživateli selhalo
|
||||||
err_xsessions_dir = nepodařilo se najít složku relací
|
err_xsessions_dir = nepodařilo se najít složku relací
|
||||||
err_xsessions_open = nepodařilo se otevřít složku relací
|
err_xsessions_open = nepodařilo se otevřít složku relací
|
||||||
f1 = F1 vypnout
|
|
||||||
f2 = F2 restartovat
|
|
||||||
login = uživatel
|
login = uživatel
|
||||||
logout = odhlášen
|
logout = odhlášen
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
password = heslo
|
password = heslo
|
||||||
|
restart = restartovat
|
||||||
shell = příkazový řádek
|
shell = příkazový řádek
|
||||||
|
shutdown = vypnout
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ err_user_init = Initialisierung des Nutzers fehlgeschlagen
|
|||||||
err_user_uid = Setzen der Benutzer Id fehlgeschlagen
|
err_user_uid = Setzen der Benutzer Id fehlgeschlagen
|
||||||
err_xsessions_dir = Fehler beim finden des Sitzungsordners
|
err_xsessions_dir = Fehler beim finden des Sitzungsordners
|
||||||
err_xsessions_open = Fehler beim öffnen des Sitzungsordners
|
err_xsessions_open = Fehler beim öffnen des Sitzungsordners
|
||||||
f1 = F1 Herunterfahren
|
|
||||||
f2 = F2 Neustarten
|
|
||||||
login = Anmelden
|
login = Anmelden
|
||||||
logout = Abgemeldet
|
logout = Abgemeldet
|
||||||
numlock = Numtaste
|
numlock = Numtaste
|
||||||
password = Passwort
|
password = Passwort
|
||||||
|
restart = Neustarten
|
||||||
shell = shell
|
shell = shell
|
||||||
|
shutdown = Herunterfahren
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
authenticating = authenticating...
|
||||||
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
|
||||||
@@ -5,7 +6,9 @@ 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_mcookie = mcookie command failed
|
||||||
err_mlock = failed to lock password memory
|
err_mlock = failed to lock password memory
|
||||||
err_null = null pointer
|
err_null = null pointer
|
||||||
err_pam = pam transaction failed
|
err_pam = pam transaction failed
|
||||||
@@ -29,17 +32,24 @@ 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
|
||||||
f1 = F1 shutdown
|
insert = insert
|
||||||
f2 = F2 reboot
|
|
||||||
login = login
|
login = login
|
||||||
logout = logged out
|
logout = logged out
|
||||||
|
normal = normal
|
||||||
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
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ err_user_init = error al inicializar usuario
|
|||||||
err_user_uid = error al establecer el UID del usuario
|
err_user_uid = error al establecer el UID del usuario
|
||||||
err_xsessions_dir = error al buscar la carpeta de sesiones
|
err_xsessions_dir = error al buscar la carpeta de sesiones
|
||||||
err_xsessions_open = error al abrir la carpeta de sesiones
|
err_xsessions_open = error al abrir la carpeta de sesiones
|
||||||
f1 = F1 apagar
|
|
||||||
f2 = F2 reiniciar
|
|
||||||
login = iniciar sesión
|
login = iniciar sesión
|
||||||
logout = cerrar sesión
|
logout = cerrar sesión
|
||||||
numlock = Bloq Num
|
numlock = Bloq Num
|
||||||
password = contraseña
|
password = contraseña
|
||||||
|
restart = reiniciar
|
||||||
shell = shell
|
shell = shell
|
||||||
|
shutdown = apagar
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ 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_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
|
||||||
f1 = F1 éteindre
|
|
||||||
f2 = F2 redémarrer
|
|
||||||
login = identifiant
|
login = identifiant
|
||||||
logout = déconnection
|
logout = déconnection
|
||||||
numlock = verr.num
|
numlock = verr.num
|
||||||
password = mot de passe
|
password = mot de passe
|
||||||
|
restart = redémarrer
|
||||||
shell = shell
|
shell = shell
|
||||||
|
shutdown = éteindre
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ err_user_init = impossibile inizializzare utente
|
|||||||
err_user_uid = impossible impostare UID utente
|
err_user_uid = impossible impostare UID utente
|
||||||
err_xsessions_dir = impossibile localizzare cartella sessioni
|
err_xsessions_dir = impossibile localizzare cartella sessioni
|
||||||
err_xsessions_open = impossibile aprire cartella sessioni
|
err_xsessions_open = impossibile aprire cartella sessioni
|
||||||
f1 = F1 arresto
|
|
||||||
f2 = F2 riavvio
|
|
||||||
login = username
|
login = username
|
||||||
logout = scollegato
|
logout = scollegato
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
password = password
|
password = password
|
||||||
|
restart = riavvio
|
||||||
shell = shell
|
shell = shell
|
||||||
|
shutdown = arresto
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ err_user_init = nie udało się zainicjalizować użytkownika
|
|||||||
err_user_uid = nie udało się ustawić UID użytkownika
|
err_user_uid = nie udało się ustawić UID użytkownika
|
||||||
err_xsessions_dir = nie udało się znaleźć folderu sesji
|
err_xsessions_dir = nie udało się znaleźć folderu sesji
|
||||||
err_xsessions_open = nie udało się otworzyć folderu sesji
|
err_xsessions_open = nie udało się otworzyć folderu sesji
|
||||||
f1 = F1 wyłącz
|
|
||||||
f2 = F2 uruchom ponownie
|
|
||||||
login = login
|
login = login
|
||||||
logout = wylogowano
|
logout = wylogowano
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
password = hasło
|
password = hasło
|
||||||
|
restart = uruchom ponownie
|
||||||
shell = powłoka
|
shell = powłoka
|
||||||
|
shutdown = wyłącz
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ err_user_init = erro ao iniciar o utilizador
|
|||||||
err_user_uid = erro ao definir o UID do utilizador
|
err_user_uid = erro ao definir o UID do utilizador
|
||||||
err_xsessions_dir = erro ao localizar a pasta das sessões
|
err_xsessions_dir = erro ao localizar a pasta das sessões
|
||||||
err_xsessions_open = erro ao abrir a pasta das sessões
|
err_xsessions_open = erro ao abrir a pasta das sessões
|
||||||
f1 = F1 encerrar
|
|
||||||
f2 = F2 reiniciar
|
|
||||||
login = iniciar sessão
|
login = iniciar sessão
|
||||||
logout = terminar sessão
|
logout = terminar sessão
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
password = palavra-passe
|
password = palavra-passe
|
||||||
|
restart = reiniciar
|
||||||
shell = shell
|
shell = shell
|
||||||
|
shutdown = encerrar
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ err_user_init = não foi possível iniciar o usuário
|
|||||||
err_user_uid = não foi possível definir o UID do usuário
|
err_user_uid = não foi possível definir o UID do usuário
|
||||||
err_xsessions_dir = não foi possível encontrar a pasta das sessões
|
err_xsessions_dir = não foi possível encontrar a pasta das sessões
|
||||||
err_xsessions_open = não foi possível abrir a pasta das sessões
|
err_xsessions_open = não foi possível abrir a pasta das sessões
|
||||||
f1 = F1 desligar
|
|
||||||
f2 = F2 reiniciar
|
|
||||||
login = conectar
|
login = conectar
|
||||||
logout = desconectado
|
logout = desconectado
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
password = senha
|
password = senha
|
||||||
|
restart = reiniciar
|
||||||
shell = shell
|
shell = shell
|
||||||
|
shutdown = desligar
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ err_perm_user = nu s-a putut face downgrade permisiunilor de utilizator
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
f1 = F1 opreşte sistemul
|
|
||||||
f2 = F2 resetează
|
|
||||||
login = utilizator
|
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
|
||||||
|
|||||||
@@ -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 = не удалось открыть сессионную папку
|
||||||
f1 = F1 выключить
|
|
||||||
f2 = F2 перезагрузить
|
|
||||||
login = логин
|
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
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ err_user_init = neuspijensa inicijalizacija korisnika
|
|||||||
err_user_uid = neuspijesno postavljanje UID-a korisnika
|
err_user_uid = neuspijesno postavljanje UID-a korisnika
|
||||||
err_xsessions_dir = neuspijesno pronalazenje foldera sesija
|
err_xsessions_dir = neuspijesno pronalazenje foldera sesija
|
||||||
err_xsessions_open = neuspijesno otvaranje foldera sesija
|
err_xsessions_open = neuspijesno otvaranje foldera sesija
|
||||||
f1 = F1 ugasi
|
|
||||||
f2 = F2 ponovo pokreni
|
|
||||||
login = korisnik
|
login = korisnik
|
||||||
logout = izlogovan
|
logout = izlogovan
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
password = lozinka
|
password = lozinka
|
||||||
|
restart = ponovo pokreni
|
||||||
shell = shell
|
shell = shell
|
||||||
|
shutdown = ugasi
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ err_user_init = misslyckades att initialisera användaren
|
|||||||
err_user_uid = misslyckades att ställa in användar-UID
|
err_user_uid = misslyckades att ställa in användar-UID
|
||||||
err_xsessions_dir = misslyckades att hitta sessionskatalog
|
err_xsessions_dir = misslyckades att hitta sessionskatalog
|
||||||
err_xsessions_open = misslyckades att öppna sessionskatalog
|
err_xsessions_open = misslyckades att öppna sessionskatalog
|
||||||
f1 = F1 stäng av
|
login = inloggning
|
||||||
f2 = F2 starta om
|
|
||||||
login = inloggad
|
|
||||||
logout = utloggad
|
logout = utloggad
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
password = lösenord
|
password = lösenord
|
||||||
|
restart = starta om
|
||||||
shell = skal
|
shell = skal
|
||||||
|
shutdown = stäng av
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ err_user_init = kullanici oturumu baslatilamadi
|
|||||||
err_user_uid = kullanici icin UID ayarlanamadi
|
err_user_uid = kullanici icin UID ayarlanamadi
|
||||||
err_xsessions_dir = oturumlar klasoru bulunamadi
|
err_xsessions_dir = oturumlar klasoru bulunamadi
|
||||||
err_xsessions_open = oturumlar klasoru acilamadi
|
err_xsessions_open = oturumlar klasoru acilamadi
|
||||||
f1 = F1 makineyi kapat
|
|
||||||
f2 = F2 yeniden baslat
|
|
||||||
login = kullanici
|
login = kullanici
|
||||||
logout = oturumdan cikis yapildi
|
logout = oturumdan cikis yapildi
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
password = sifre
|
password = sifre
|
||||||
|
restart = yeniden baslat
|
||||||
shell = shell
|
shell = shell
|
||||||
|
shutdown = makineyi kapat
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -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 = не вдалося відкрити каталог сесій
|
||||||
f1 = F1 вимкнути
|
|
||||||
f2 = F2 перезавантажити
|
|
||||||
login = логін
|
login = логін
|
||||||
logout = вийти
|
logout = вийти
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
password = пароль
|
password = пароль
|
||||||
|
restart = перезавантажити
|
||||||
shell = оболонка
|
shell = оболонка
|
||||||
|
shutdown = вимкнути
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
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@tty2.service
|
After=getty@tty2.service
|
||||||
|
Conflicts=getty@tty2.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=idle
|
Type=idle
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ case $SHELL in
|
|||||||
;;
|
;;
|
||||||
*/fish)
|
*/fish)
|
||||||
[ -f /etc/profile ] && . /etc/profile
|
[ -f /etc/profile ] && . /etc/profile
|
||||||
|
[ -f $HOME/.profile ] && . $HOME/.profile
|
||||||
xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX`
|
xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX`
|
||||||
$SHELL --login -c "/bin/sh -c 'export -p' > $xsess_tmp"
|
$SHELL --login -c "/bin/sh -c 'export -p' > $xsess_tmp"
|
||||||
. $xsess_tmp
|
. $xsess_tmp
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#! /bin/sh
|
#!/bin/sh
|
||||||
# Xsession - run as user
|
# Xsession - run as user
|
||||||
# Copyright (C) 2016 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
|
# Copyright (C) 2016 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
|
||||||
|
|
||||||
@@ -40,6 +40,7 @@ case $SHELL in
|
|||||||
;;
|
;;
|
||||||
*/fish)
|
*/fish)
|
||||||
[ -f /etc/profile ] && . /etc/profile
|
[ -f /etc/profile ] && . /etc/profile
|
||||||
|
[ -f $HOME/.profile ] && . $HOME/.profile
|
||||||
xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX`
|
xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX`
|
||||||
$SHELL --login -c "/bin/sh -c 'export -p' > $xsess_tmp"
|
$SHELL --login -c "/bin/sh -c 'export -p' > $xsess_tmp"
|
||||||
. $xsess_tmp
|
. $xsess_tmp
|
||||||
@@ -82,6 +83,10 @@ if [ -d "$xsessionddir" ]; then
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -f "$USERXSESSION" ]; then
|
||||||
|
. "$USERXSESSION"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -d /etc/X11/Xresources ]; then
|
if [ -d /etc/X11/Xresources ]; then
|
||||||
for i in /etc/X11/Xresources/*; do
|
for i in /etc/X11/Xresources/*; do
|
||||||
[ -f $i ] && xrdb -merge $i
|
[ -f $i ] && xrdb -merge $i
|
||||||
@@ -92,10 +97,6 @@ fi
|
|||||||
[ -f $HOME/.Xresources ] && xrdb -merge $HOME/.Xresources
|
[ -f $HOME/.Xresources ] && xrdb -merge $HOME/.Xresources
|
||||||
[ -f $XDG_CONFIG_HOME/X11/Xresources ] && xrdb -merge $XDG_CONFIG_HOME/X11/Xresources
|
[ -f $XDG_CONFIG_HOME/X11/Xresources ] && xrdb -merge $XDG_CONFIG_HOME/X11/Xresources
|
||||||
|
|
||||||
if [ -f "$USERXSESSION" ]; then
|
|
||||||
. "$USERXSESSION"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$*" ]; then
|
if [ -z "$*" ]; then
|
||||||
exec xmessage -center -buttons OK:0 -default OK "Sorry, $DESKTOP_SESSION is no valid session."
|
exec xmessage -center -buttons OK:0 -default OK "Sorry, $DESKTOP_SESSION is no valid session."
|
||||||
else
|
else
|
||||||
|
|||||||
39
src/SharedError.zig
Normal file
39
src/SharedError.zig
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
84
src/animations/Doom.zig
Normal file
84
src/animations/Doom.zig
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
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 = [_]termbox.tb_cell{
|
||||||
|
utils.initCell(' ', 9, 0),
|
||||||
|
utils.initCell(0x2591, 2, 0), // Red
|
||||||
|
utils.initCell(0x2592, 2, 0), // Red
|
||||||
|
utils.initCell(0x2593, 2, 0), // Red
|
||||||
|
utils.initCell(0x2588, 2, 0), // Red
|
||||||
|
utils.initCell(0x2591, 4, 2), // Yellow
|
||||||
|
utils.initCell(0x2592, 4, 2), // Yellow
|
||||||
|
utils.initCell(0x2593, 4, 2), // Yellow
|
||||||
|
utils.initCell(0x2588, 4, 2), // Yellow
|
||||||
|
utils.initCell(0x2591, 8, 4), // White
|
||||||
|
utils.initCell(0x2592, 8, 4), // White
|
||||||
|
utils.initCell(0x2593, 8, 4), // White
|
||||||
|
utils.initCell(0x2588, 8, 4), // White
|
||||||
|
};
|
||||||
|
|
||||||
|
allocator: Allocator,
|
||||||
|
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] = FIRE[buffer_dest];
|
||||||
|
self.terminal_buffer.buffer[source] = FIRE[buffer_source];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initBuffer(buffer: []u8, width: u64) 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);
|
||||||
|
}
|
||||||
181
src/animations/Matrix.zig
Normal file
181
src/animations/Matrix.zig
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
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,
|
||||||
|
|
||||||
|
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer) !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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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: u64 = 0;
|
||||||
|
while (x < self.terminal_buffer.width) : (x += 2) {
|
||||||
|
var tail: u64 = 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: u64 = 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: u64 = 0;
|
||||||
|
while (x < buf_width) : (x += 2) {
|
||||||
|
var y: u64 = 1;
|
||||||
|
while (y <= self.terminal_buffer.height) : (y += 1) {
|
||||||
|
const dot = self.dots[buf_width * y + x];
|
||||||
|
var fg: u16 = @intCast(termbox.TB_GREEN);
|
||||||
|
|
||||||
|
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: u64, height: u64, random: Random) void {
|
||||||
|
var y: u64 = 0;
|
||||||
|
while (y <= height) : (y += 1) {
|
||||||
|
var x: u64 = 0;
|
||||||
|
while (x < width) : (x += 2) {
|
||||||
|
dots[y * width + x].value = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var x: u64 = 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 = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
520
src/auth.zig
Normal file
520
src/auth.zig
Normal file
@@ -0,0 +1,520 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const enums = @import("enums.zig");
|
||||||
|
const interop = @import("interop.zig");
|
||||||
|
const TerminalBuffer = @import("tui/TerminalBuffer.zig");
|
||||||
|
const Desktop = @import("tui/components/Desktop.zig");
|
||||||
|
const Text = @import("tui/components/Text.zig");
|
||||||
|
const Config = @import("config/Config.zig");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const utmp = interop.utmp;
|
||||||
|
const Utmp = utmp.utmp;
|
||||||
|
const SharedError = @import("SharedError.zig");
|
||||||
|
|
||||||
|
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, current_environment: Desktop.Environment, login: [:0]const u8, password: [:0]const u8) !void {
|
||||||
|
var tty_buffer: [3]u8 = undefined;
|
||||||
|
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty});
|
||||||
|
|
||||||
|
var pam_tty_buffer: [6]u8 = undefined;
|
||||||
|
const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{config.tty});
|
||||||
|
|
||||||
|
// Set the XDG environment variables
|
||||||
|
setXdgSessionEnv(current_environment.display_server);
|
||||||
|
try setXdgEnv(tty_str, current_environment.xdg_session_desktop, current_environment.xdg_desktop_names 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;
|
||||||
|
|
||||||
|
var status = interop.pam.pam_start(config.service_name.ptr, null, &conv, &handle);
|
||||||
|
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||||
|
defer _ = interop.pam.pam_end(handle, status);
|
||||||
|
|
||||||
|
// Set PAM_TTY as the current TTY. This is required in case it isn't being set by another PAM module
|
||||||
|
status = interop.pam.pam_set_item(handle, interop.pam.PAM_TTY, pam_tty_str.ptr);
|
||||||
|
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||||
|
|
||||||
|
// Do the PAM routine
|
||||||
|
status = interop.pam.pam_authenticate(handle, 0);
|
||||||
|
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||||
|
|
||||||
|
status = interop.pam.pam_acct_mgmt(handle, 0);
|
||||||
|
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||||
|
|
||||||
|
status = interop.pam.pam_setcred(handle, interop.pam.PAM_ESTABLISH_CRED);
|
||||||
|
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||||
|
defer status = interop.pam.pam_setcred(handle, interop.pam.PAM_DELETE_CRED);
|
||||||
|
|
||||||
|
status = interop.pam.pam_open_session(handle, 0);
|
||||||
|
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||||
|
defer status = interop.pam.pam_close_session(handle, 0);
|
||||||
|
|
||||||
|
var pwd: *interop.passwd = undefined;
|
||||||
|
{
|
||||||
|
defer interop.endpwent();
|
||||||
|
|
||||||
|
// Get password structure from username
|
||||||
|
pwd = interop.getpwnam(login.ptr) orelse return error.GetPasswordNameFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set user shell if it hasn't already been set
|
||||||
|
if (pwd.pw_shell[0] == 0) {
|
||||||
|
interop.setusershell();
|
||||||
|
pwd.pw_shell = interop.getusershell();
|
||||||
|
interop.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, current_environment) catch |e| {
|
||||||
|
shared_err.writeError(e);
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
std.process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var entry = std.mem.zeroes(Utmp);
|
||||||
|
|
||||||
|
{
|
||||||
|
// If an error occurs here, we can send SIGTERM to the session
|
||||||
|
errdefer cleanup: {
|
||||||
|
_ = std.posix.kill(child_pid, std.posix.SIG.TERM) catch break :cleanup;
|
||||||
|
_ = std.posix.waitpid(child_pid, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we receive SIGTERM, forward it to child_pid
|
||||||
|
const act = std.posix.Sigaction{
|
||||||
|
.handler = .{ .handler = &sessionSignalHandler },
|
||||||
|
.mask = std.posix.empty_sigset,
|
||||||
|
.flags = 0,
|
||||||
|
};
|
||||||
|
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);
|
||||||
|
|
||||||
|
try resetTerminal(pwd.pw_shell, config.term_reset_cmd);
|
||||||
|
|
||||||
|
if (shared_err.readError()) |err| return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn startSession(
|
||||||
|
config: Config,
|
||||||
|
pwd: *interop.passwd,
|
||||||
|
handle: ?*interop.pam.pam_handle,
|
||||||
|
current_environment: Desktop.Environment,
|
||||||
|
) !void {
|
||||||
|
const status = interop.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.putenv(env_var.?);
|
||||||
|
|
||||||
|
// Execute what the user requested
|
||||||
|
std.posix.chdirZ(pwd.pw_dir) catch return error.ChangeDirectoryFailed;
|
||||||
|
|
||||||
|
try resetTerminal(pwd.pw_shell, config.term_reset_cmd);
|
||||||
|
|
||||||
|
switch (current_environment.display_server) {
|
||||||
|
.wayland => try executeWaylandCmd(pwd.pw_shell, config.wayland_cmd, current_environment.cmd),
|
||||||
|
.shell => try executeShellCmd(pwd.pw_shell),
|
||||||
|
.xinitrc, .x11 => {
|
||||||
|
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, current_environment.cmd, vt);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initEnv(pwd: *interop.passwd, path_env: ?[:0]const u8) !void {
|
||||||
|
_ = interop.setenv("HOME", pwd.pw_dir, 1);
|
||||||
|
_ = interop.setenv("PWD", pwd.pw_dir, 1);
|
||||||
|
_ = interop.setenv("SHELL", pwd.pw_shell, 1);
|
||||||
|
_ = interop.setenv("USER", pwd.pw_name, 1);
|
||||||
|
_ = interop.setenv("LOGNAME", pwd.pw_name, 1);
|
||||||
|
|
||||||
|
if (path_env) |path| {
|
||||||
|
const status = interop.setenv("PATH", path, 1);
|
||||||
|
if (status != 0) return error.SetPathFailed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setXdgSessionEnv(display_server: enums.DisplayServer) void {
|
||||||
|
_ = interop.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 {
|
||||||
|
const uid = interop.getuid();
|
||||||
|
var uid_buffer: [10 + @sizeOf(u32) + 1]u8 = undefined;
|
||||||
|
const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid});
|
||||||
|
|
||||||
|
if (!std.mem.eql(u8, xdg_desktop_names, "")) _ = interop.setenv("XDG_CURRENT_DESKTOP", xdg_desktop_names.ptr, 0);
|
||||||
|
_ = interop.setenv("XDG_RUNTIME_DIR", uid_str.ptr, 0);
|
||||||
|
_ = interop.setenv("XDG_SESSION_CLASS", "user", 0);
|
||||||
|
_ = interop.setenv("XDG_SESSION_ID", "1", 0);
|
||||||
|
if (!std.mem.eql(u8, desktop_name, "")) _ = interop.setenv("XDG_SESSION_DESKTOP", desktop_name.ptr, 0);
|
||||||
|
_ = interop.setenv("XDG_SEAT", "seat0", 0);
|
||||||
|
_ = interop.setenv("XDG_VTNR", tty_str.ptr, 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.?.ptr;
|
||||||
|
},
|
||||||
|
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.?.ptr;
|
||||||
|
},
|
||||||
|
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 resetTerminal(shell: [*:0]const u8, term_reset_cmd: [:0]const u8) !void {
|
||||||
|
const pid = try std.posix.fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
const args = [_:null]?[*:0]const u8{ shell, "-c", term_reset_cmd };
|
||||||
|
std.posix.execveZ(shell, &args, std.c.environ) catch {};
|
||||||
|
std.process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = std.posix.waitpid(pid, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getFreeDisplay() !u8 {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mcookie(shell: [*:0]const u8, cmd: [:0]const u8) ![32]u8 {
|
||||||
|
const pipe = try std.posix.pipe();
|
||||||
|
defer std.posix.close(pipe[1]);
|
||||||
|
|
||||||
|
const output = std.fs.File{ .handle = pipe[0] };
|
||||||
|
defer output.close();
|
||||||
|
|
||||||
|
const pid = try std.posix.fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
std.posix.close(pipe[0]);
|
||||||
|
|
||||||
|
std.posix.dup2(pipe[1], std.posix.STDOUT_FILENO) catch std.process.exit(1);
|
||||||
|
std.posix.close(pipe[1]);
|
||||||
|
|
||||||
|
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd };
|
||||||
|
std.posix.execveZ(shell, &args, std.c.environ) catch {};
|
||||||
|
std.process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = std.posix.waitpid(pid, 0);
|
||||||
|
|
||||||
|
if (result.status != 0) return error.McookieFailed;
|
||||||
|
|
||||||
|
var buf: [32]u8 = undefined;
|
||||||
|
const len = try output.read(&buf);
|
||||||
|
if (len != 32) return error.McookieFailed;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, xauth_cmd: []const u8, mcookie_cmd: [:0]const u8) !void {
|
||||||
|
var pwd_buf: [100]u8 = undefined;
|
||||||
|
const pwd = try std.fmt.bufPrintZ(&pwd_buf, "{s}", .{pw_dir});
|
||||||
|
|
||||||
|
const xauthority = try createXauthFile(pwd);
|
||||||
|
_ = interop.setenv("XAUTHORITY", xauthority, 1);
|
||||||
|
_ = interop.setenv("DISPLAY", display_name, 1);
|
||||||
|
|
||||||
|
const mcookie_output = try mcookie(shell, mcookie_cmd);
|
||||||
|
|
||||||
|
const pid = try std.posix.fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
var cmd_buffer: [1024]u8 = undefined;
|
||||||
|
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . {s}", .{ xauth_cmd, display_name, mcookie_output }) 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) !void {
|
||||||
|
const args = [_:null]?[*:0]const u8{shell};
|
||||||
|
return std.posix.execveZ(shell, &args, std.c.environ);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn executeWaylandCmd(shell: [*:0]const u8, wayland_cmd: []const u8, desktop_cmd: []const u8) !void {
|
||||||
|
var cmd_buffer: [1024]u8 = undefined;
|
||||||
|
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s}", .{ wayland_cmd, desktop_cmd });
|
||||||
|
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
|
||||||
|
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.xauth_cmd, config.mcookie_cmd);
|
||||||
|
|
||||||
|
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}", .{ config.x_cmd, display_name, vt }) catch std.process.exit(1);
|
||||||
|
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
|
||||||
|
std.posix.execveZ(shell, &args, std.c.environ) catch {};
|
||||||
|
std.process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ok: c_int = undefined;
|
||||||
|
var xcb: ?*interop.xcb.xcb_connection_t = null;
|
||||||
|
while (ok != 0) {
|
||||||
|
xcb = interop.xcb.xcb_connect(null, null);
|
||||||
|
ok = interop.xcb.xcb_connection_has_error(xcb);
|
||||||
|
std.posix.kill(pid, 0) catch |e| {
|
||||||
|
if (e == error.ProcessNotFound and ok != 0) return error.XcbConnectionFailed;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// X Server detaches from the process.
|
||||||
|
// PID can be fetched from /tmp/X{d}.lock
|
||||||
|
const x_pid = try getXPid(display_num);
|
||||||
|
|
||||||
|
xorg_pid = try std.posix.fork();
|
||||||
|
if (xorg_pid == 0) {
|
||||||
|
var cmd_buffer: [1024]u8 = undefined;
|
||||||
|
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s}", .{ config.x_cmd_setup, desktop_cmd }) catch std.process.exit(1);
|
||||||
|
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
|
||||||
|
std.posix.execveZ(shell, &args, std.c.environ) catch {};
|
||||||
|
std.process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we receive SIGTERM, clean up by killing the xorg_pid process
|
||||||
|
const act = std.posix.Sigaction{
|
||||||
|
.handler = .{ .handler = &xorgSignalHandler },
|
||||||
|
.mask = std.posix.empty_sigset,
|
||||||
|
.flags = 0,
|
||||||
|
};
|
||||||
|
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 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: [32]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: [32]u8 = undefined;
|
||||||
|
_ = try std.fmt.bufPrintZ(&username_buf, "{s}", .{username});
|
||||||
|
|
||||||
|
entry.ut_user = username_buf;
|
||||||
|
|
||||||
|
var host: [256]u8 = undefined;
|
||||||
|
host[0] = 0;
|
||||||
|
entry.ut_host = host;
|
||||||
|
|
||||||
|
var tv: std.c.timeval = undefined;
|
||||||
|
_ = std.c.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.setutent();
|
||||||
|
_ = utmp.pututline(entry);
|
||||||
|
utmp.endutent();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn removeUtmpEntry(entry: *Utmp) void {
|
||||||
|
entry.ut_type = utmp.DEAD_PROCESS;
|
||||||
|
entry.ut_line[0] = 0;
|
||||||
|
entry.ut_user[0] = 0;
|
||||||
|
utmp.setutent();
|
||||||
|
_ = utmp.pututline(entry);
|
||||||
|
utmp.endutent();
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
146
src/bigclock.h
146
src/bigclock.h
@@ -1,146 +0,0 @@
|
|||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define CLOCK_W 5
|
|
||||||
#define CLOCK_H 5
|
|
||||||
|
|
||||||
#if defined(__linux__) || defined(__FreeBSD__)
|
|
||||||
#define X 0x2593
|
|
||||||
#define _ 0x0000
|
|
||||||
#else
|
|
||||||
#define X '#'
|
|
||||||
#define _ 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if CLOCK_W == 5 && CLOCK_H == 5
|
|
||||||
|
|
||||||
uint32_t CLOCK_0[] = {
|
|
||||||
X,X,X,X,X,
|
|
||||||
X,X,_,X,X,
|
|
||||||
X,X,_,X,X,
|
|
||||||
X,X,_,X,X,
|
|
||||||
X,X,X,X,X
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t CLOCK_1[] = {
|
|
||||||
_,_,_,X,X,
|
|
||||||
_,_,_,X,X,
|
|
||||||
_,_,_,X,X,
|
|
||||||
_,_,_,X,X,
|
|
||||||
_,_,_,X,X
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t CLOCK_2[] = {
|
|
||||||
X,X,X,X,X,
|
|
||||||
_,_,_,X,X,
|
|
||||||
X,X,X,X,X,
|
|
||||||
X,X,_,_,_,
|
|
||||||
X,X,X,X,X
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t CLOCK_3[] = {
|
|
||||||
X,X,X,X,X,
|
|
||||||
_,_,_,X,X,
|
|
||||||
X,X,X,X,X,
|
|
||||||
_,_,_,X,X,
|
|
||||||
X,X,X,X,X
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t CLOCK_4[] = {
|
|
||||||
X,X,_,X,X,
|
|
||||||
X,X,_,X,X,
|
|
||||||
X,X,X,X,X,
|
|
||||||
_,_,_,X,X,
|
|
||||||
_,_,_,X,X
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t CLOCK_5[] = {
|
|
||||||
X,X,X,X,X,
|
|
||||||
X,X,_,_,_,
|
|
||||||
X,X,X,X,X,
|
|
||||||
_,_,_,X,X,
|
|
||||||
X,X,X,X,X
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t CLOCK_6[] = {
|
|
||||||
X,X,X,X,X,
|
|
||||||
X,X,_,_,_,
|
|
||||||
X,X,X,X,X,
|
|
||||||
X,X,_,X,X,
|
|
||||||
X,X,X,X,X,
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t CLOCK_7[] = {
|
|
||||||
X,X,X,X,X,
|
|
||||||
_,_,_,X,X,
|
|
||||||
_,_,_,X,X,
|
|
||||||
_,_,_,X,X,
|
|
||||||
_,_,_,X,X
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t CLOCK_8[] = {
|
|
||||||
X,X,X,X,X,
|
|
||||||
X,X,_,X,X,
|
|
||||||
X,X,X,X,X,
|
|
||||||
X,X,_,X,X,
|
|
||||||
X,X,X,X,X
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t CLOCK_9[] = {
|
|
||||||
X,X,X,X,X,
|
|
||||||
X,X,_,X,X,
|
|
||||||
X,X,X,X,X,
|
|
||||||
_,_,_,X,X,
|
|
||||||
X,X,X,X,X
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t CLOCK_S[] = {
|
|
||||||
_,_,_,_,_,
|
|
||||||
_,_,X,_,_,
|
|
||||||
_,_,_,_,_,
|
|
||||||
_,_,X,_,_,
|
|
||||||
_,_,_,_,_
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t CLOCK_E[] = {
|
|
||||||
_,_,_,_,_,
|
|
||||||
_,_,_,_,_,
|
|
||||||
_,_,_,_,_,
|
|
||||||
_,_,_,_,_,
|
|
||||||
_,_,_,_,_
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef X
|
|
||||||
#undef _
|
|
||||||
|
|
||||||
static inline uint32_t* CLOCK_N(char c)
|
|
||||||
{
|
|
||||||
switch(c)
|
|
||||||
{
|
|
||||||
case '0':
|
|
||||||
return CLOCK_0;
|
|
||||||
case '1':
|
|
||||||
return CLOCK_1;
|
|
||||||
case '2':
|
|
||||||
return CLOCK_2;
|
|
||||||
case '3':
|
|
||||||
return CLOCK_3;
|
|
||||||
case '4':
|
|
||||||
return CLOCK_4;
|
|
||||||
case '5':
|
|
||||||
return CLOCK_5;
|
|
||||||
case '6':
|
|
||||||
return CLOCK_6;
|
|
||||||
case '7':
|
|
||||||
return CLOCK_7;
|
|
||||||
case '8':
|
|
||||||
return CLOCK_8;
|
|
||||||
case '9':
|
|
||||||
return CLOCK_9;
|
|
||||||
case ':':
|
|
||||||
return CLOCK_S;
|
|
||||||
default:
|
|
||||||
return CLOCK_E;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
140
src/bigclock.zig
Normal file
140
src/bigclock.zig
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const interop = @import("interop.zig");
|
||||||
|
const utils = @import("tui/utils.zig");
|
||||||
|
|
||||||
|
const termbox = interop.termbox;
|
||||||
|
|
||||||
|
const X: u32 = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) 0x2593 else '#';
|
||||||
|
const O: u32 = 0;
|
||||||
|
|
||||||
|
pub const WIDTH = 5;
|
||||||
|
pub const HEIGHT = 5;
|
||||||
|
pub const SIZE = WIDTH * HEIGHT;
|
||||||
|
|
||||||
|
// zig fmt: off
|
||||||
|
const ZERO = [_]u21{
|
||||||
|
X,X,X,X,X,
|
||||||
|
X,X,O,X,X,
|
||||||
|
X,X,O,X,X,
|
||||||
|
X,X,O,X,X,
|
||||||
|
X,X,X,X,X,
|
||||||
|
};
|
||||||
|
const ONE = [_]u21{
|
||||||
|
O,O,O,X,X,
|
||||||
|
O,O,O,X,X,
|
||||||
|
O,O,O,X,X,
|
||||||
|
O,O,O,X,X,
|
||||||
|
O,O,O,X,X,
|
||||||
|
};
|
||||||
|
const TWO = [_]u21{
|
||||||
|
X,X,X,X,X,
|
||||||
|
O,O,O,X,X,
|
||||||
|
X,X,X,X,X,
|
||||||
|
X,X,O,O,O,
|
||||||
|
X,X,X,X,X,
|
||||||
|
};
|
||||||
|
const THREE = [_]u21{
|
||||||
|
X,X,X,X,X,
|
||||||
|
O,O,O,X,X,
|
||||||
|
X,X,X,X,X,
|
||||||
|
O,O,O,X,X,
|
||||||
|
X,X,X,X,X,
|
||||||
|
};
|
||||||
|
const FOUR = [_]u21{
|
||||||
|
X,X,O,X,X,
|
||||||
|
X,X,O,X,X,
|
||||||
|
X,X,X,X,X,
|
||||||
|
O,O,O,X,X,
|
||||||
|
O,O,O,X,X,
|
||||||
|
};
|
||||||
|
const FIVE = [_]u21{
|
||||||
|
X,X,X,X,X,
|
||||||
|
X,X,O,O,O,
|
||||||
|
X,X,X,X,X,
|
||||||
|
O,O,O,X,X,
|
||||||
|
X,X,X,X,X,
|
||||||
|
};
|
||||||
|
const SIX = [_]u21{
|
||||||
|
X,X,X,X,X,
|
||||||
|
X,X,O,O,O,
|
||||||
|
X,X,X,X,X,
|
||||||
|
X,X,O,X,X,
|
||||||
|
X,X,X,X,X,
|
||||||
|
};
|
||||||
|
const SEVEN = [_]u21{
|
||||||
|
X,X,X,X,X,
|
||||||
|
O,O,O,X,X,
|
||||||
|
O,O,O,X,X,
|
||||||
|
O,O,O,X,X,
|
||||||
|
O,O,O,X,X,
|
||||||
|
};
|
||||||
|
const EIGHT = [_]u21{
|
||||||
|
X,X,X,X,X,
|
||||||
|
X,X,O,X,X,
|
||||||
|
X,X,X,X,X,
|
||||||
|
X,X,O,X,X,
|
||||||
|
X,X,X,X,X,
|
||||||
|
};
|
||||||
|
const NINE = [_]u21{
|
||||||
|
X,X,X,X,X,
|
||||||
|
X,X,O,X,X,
|
||||||
|
X,X,X,X,X,
|
||||||
|
O,O,O,X,X,
|
||||||
|
X,X,X,X,X,
|
||||||
|
};
|
||||||
|
const S = [_]u21{
|
||||||
|
O,O,O,O,O,
|
||||||
|
O,O,X,O,O,
|
||||||
|
O,O,O,O,O,
|
||||||
|
O,O,X,O,O,
|
||||||
|
O,O,O,O,O,
|
||||||
|
};
|
||||||
|
const E = [_]u21{
|
||||||
|
O,O,O,O,O,
|
||||||
|
O,O,O,O,O,
|
||||||
|
O,O,O,O,O,
|
||||||
|
O,O,O,O,O,
|
||||||
|
O,O,O,O,O,
|
||||||
|
};
|
||||||
|
// zig fmt: on
|
||||||
|
|
||||||
|
pub fn clockCell(animate: bool, char: u8, fg: u8, bg: u8) [SIZE]termbox.tb_cell {
|
||||||
|
var cells: [SIZE]termbox.tb_cell = undefined;
|
||||||
|
|
||||||
|
var tv: std.c.timeval = undefined;
|
||||||
|
_ = std.c.gettimeofday(&tv, null);
|
||||||
|
|
||||||
|
const clock_chars = toBigNumber(if (animate and char == ':' and @divTrunc(tv.tv_usec, 500000) != 0) ' ' else char);
|
||||||
|
for (0..cells.len) |i| cells[i] = utils.initCell(clock_chars[i], fg, bg);
|
||||||
|
|
||||||
|
return cells;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alphaBlit(buffer: [*]termbox.tb_cell, x: u64, y: u64, tb_width: u64, tb_height: u64, cells: [SIZE]termbox.tb_cell) void {
|
||||||
|
if (x + WIDTH >= tb_width or y + HEIGHT >= tb_height) return;
|
||||||
|
|
||||||
|
for (0..HEIGHT) |yy| {
|
||||||
|
for (0..WIDTH) |xx| {
|
||||||
|
const cell = cells[yy * WIDTH + xx];
|
||||||
|
if (cell.ch != 0) buffer[(y + yy) * tb_width + (x + xx)] = cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toBigNumber(char: u8) []const u21 {
|
||||||
|
return switch (char) {
|
||||||
|
'0' => &ZERO,
|
||||||
|
'1' => &ONE,
|
||||||
|
'2' => &TWO,
|
||||||
|
'3' => &THREE,
|
||||||
|
'4' => &FOUR,
|
||||||
|
'5' => &FIVE,
|
||||||
|
'6' => &SIX,
|
||||||
|
'7' => &SEVEN,
|
||||||
|
'8' => &EIGHT,
|
||||||
|
'9' => &NINE,
|
||||||
|
':' => &S,
|
||||||
|
else => &E,
|
||||||
|
};
|
||||||
|
}
|
||||||
378
src/config.c
378
src/config.c
@@ -1,378 +0,0 @@
|
|||||||
#include "configator.h"
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.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)
|
|
||||||
{
|
|
||||||
*((uint8_t*)data) = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*((uint8_t*)data) = atoi(*pars);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void config_handle_u16(void* data, char** pars, const int pars_count)
|
|
||||||
{
|
|
||||||
if (strcmp(*pars, "") == 0)
|
|
||||||
{
|
|
||||||
*((uint16_t*)data) = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*((uint16_t*)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},
|
|
||||||
{"bigclock", &config.bigclock, config_handle_bool},
|
|
||||||
{"blank_box", &config.blank_box, config_handle_bool},
|
|
||||||
{"blank_password", &config.blank_password, config_handle_bool},
|
|
||||||
{"clock", &config.clock, config_handle_str},
|
|
||||||
{"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},
|
|
||||||
{"hide_f1_commands", &config.hide_f1_commands, 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},
|
|
||||||
{"xinitrc", &config.xinitrc, 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.bigclock = false;
|
|
||||||
config.blank_box = true;
|
|
||||||
config.blank_password = false;
|
|
||||||
config.clock = NULL;
|
|
||||||
config.console_dev = strdup("/dev/console");
|
|
||||||
config.default_input = LOGIN_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.xinitrc = strdup("~/.xinitrc");
|
|
||||||
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.clock);
|
|
||||||
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.xinitrc);
|
|
||||||
free(config.x_cmd_setup);
|
|
||||||
free(config.xauth_cmd);
|
|
||||||
free(config.xsessions);
|
|
||||||
}
|
|
||||||
116
src/config.h
116
src/config.h
@@ -1,116 +0,0 @@
|
|||||||
#ifndef H_LY_CONFIG
|
|
||||||
#define H_LY_CONFIG
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.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;
|
|
||||||
uint8_t animation;
|
|
||||||
char asterisk;
|
|
||||||
uint8_t bg;
|
|
||||||
bool bigclock;
|
|
||||||
bool blank_box;
|
|
||||||
bool blank_password;
|
|
||||||
char* clock;
|
|
||||||
char* console_dev;
|
|
||||||
uint8_t default_input;
|
|
||||||
uint8_t fg;
|
|
||||||
bool hide_borders;
|
|
||||||
bool hide_f1_commands;
|
|
||||||
uint8_t input_len;
|
|
||||||
char* lang;
|
|
||||||
bool load;
|
|
||||||
uint8_t margin_box_h;
|
|
||||||
uint8_t margin_box_v;
|
|
||||||
uint8_t max_desktop_len;
|
|
||||||
uint8_t max_login_len;
|
|
||||||
uint8_t max_password_len;
|
|
||||||
char* mcookie_cmd;
|
|
||||||
uint16_t min_refresh_delta;
|
|
||||||
char* path;
|
|
||||||
char* restart_cmd;
|
|
||||||
bool save;
|
|
||||||
char* save_file;
|
|
||||||
char* service_name;
|
|
||||||
char* shutdown_cmd;
|
|
||||||
char* term_reset_cmd;
|
|
||||||
uint8_t tty;
|
|
||||||
char* wayland_cmd;
|
|
||||||
bool wayland_specifier;
|
|
||||||
char* waylandsessions;
|
|
||||||
char* x_cmd;
|
|
||||||
char* xinitrc;
|
|
||||||
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
|
|
||||||
53
src/config/Config.zig
Normal file
53
src/config/Config.zig
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
const build_options = @import("build_options");
|
||||||
|
const enums = @import("../enums.zig");
|
||||||
|
|
||||||
|
const Animation = enums.Animation;
|
||||||
|
const Input = enums.Input;
|
||||||
|
|
||||||
|
animation: Animation = .none,
|
||||||
|
asterisk: u8 = '*',
|
||||||
|
bg: u8 = 0,
|
||||||
|
bigclock: bool = false,
|
||||||
|
blank_box: bool = true,
|
||||||
|
border_fg: u8 = 8,
|
||||||
|
box_title: ?[]const u8 = null,
|
||||||
|
clear_password: bool = false,
|
||||||
|
clock: ?[:0]const u8 = null,
|
||||||
|
console_dev: [:0]const u8 = "/dev/console",
|
||||||
|
default_input: Input = .login,
|
||||||
|
fg: u8 = 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,
|
||||||
|
margin_box_h: u8 = 2,
|
||||||
|
margin_box_v: u8 = 1,
|
||||||
|
max_desktop_len: u8 = 100,
|
||||||
|
max_login_len: u8 = 255,
|
||||||
|
max_password_len: u8 = 255,
|
||||||
|
mcookie_cmd: [:0]const u8 = "/usr/bin/mcookie",
|
||||||
|
min_refresh_delta: u16 = 5,
|
||||||
|
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,
|
||||||
|
save_file: []const u8 = "/etc/ly/save",
|
||||||
|
service_name: [:0]const u8 = "ly",
|
||||||
|
shutdown_cmd: []const u8 = "/sbin/shutdown -a now",
|
||||||
|
shutdown_key: []const u8 = "F1",
|
||||||
|
sleep_cmd: ?[]const u8 = null,
|
||||||
|
sleep_key: []const u8 = "F3",
|
||||||
|
term_reset_cmd: [:0]const u8 = "/usr/bin/tput reset",
|
||||||
|
term_restore_cursor_cmd: []const u8 = "/usr/bin/tput cnorm",
|
||||||
|
tty: u8 = 2,
|
||||||
|
vi_mode: bool = false,
|
||||||
|
wayland_cmd: []const u8 = build_options.data_directory ++ "/wsetup.sh",
|
||||||
|
waylandsessions: []const u8 = "/usr/share/wayland-sessions",
|
||||||
|
x_cmd: []const u8 = "/usr/bin/X",
|
||||||
|
xinitrc: ?[]const u8 = "~/.xinitrc",
|
||||||
|
x_cmd_setup: []const u8 = build_options.data_directory ++ "/xsetup.sh",
|
||||||
|
xauth_cmd: []const u8 = "/usr/bin/xauth",
|
||||||
|
xsessions: []const u8 = "/usr/share/xsessions",
|
||||||
56
src/config/Lang.zig
Normal file
56
src/config/Lang.zig
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
authenticating: []const u8 = "authenticating...",
|
||||||
|
capslock: []const u8 = "capslock",
|
||||||
|
err_alloc: []const u8 = "failed memory allocation",
|
||||||
|
err_bounds: []const u8 = "out-of-bounds index",
|
||||||
|
err_chdir: []const u8 = "failed to open home folder",
|
||||||
|
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_mcookie: []const u8 = "mcookie command failed",
|
||||||
|
err_mlock: []const u8 = "failed to lock password memory",
|
||||||
|
err_null: []const u8 = "null pointer",
|
||||||
|
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",
|
||||||
|
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",
|
||||||
2
src/config/Save.zig
Normal file
2
src/config/Save.zig
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
user: ?[]const u8 = null,
|
||||||
|
session_index: ?u64 = null,
|
||||||
42
src/config/migrator.zig
Normal file
42
src/config/migrator.zig
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// The migrator ensures compatibility with <=0.6.0 configuration files
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const ini = @import("zigini");
|
||||||
|
const Save = @import("Save.zig");
|
||||||
|
|
||||||
|
pub fn configFieldHandler(_: std.mem.Allocator, field: ini.IniField) ?ini.IniField {
|
||||||
|
var mapped_field = field;
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, field.key, "blank_password")) {
|
||||||
|
mapped_field.key = "clear_password";
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapped_field;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tryMigrateSaveFile(user_buf: *[32]u8, path: []const u8) Save {
|
||||||
|
var save = Save{};
|
||||||
|
|
||||||
|
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', 32) 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', 20) catch {};
|
||||||
|
|
||||||
|
const session_index_str = session_fbs.getWritten();
|
||||||
|
var session_index: ?u64 = null;
|
||||||
|
if (session_index_str.len > 0) {
|
||||||
|
session_index = std.fmt.parseUnsigned(u64, session_index_str, 10) catch return save;
|
||||||
|
}
|
||||||
|
save.session_index = session_index;
|
||||||
|
|
||||||
|
return save;
|
||||||
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#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
|
|
||||||
997
src/draw.c
997
src/draw.c
@@ -1,997 +0,0 @@
|
|||||||
#include "dragonfail.h"
|
|
||||||
#include "termbox.h"
|
|
||||||
|
|
||||||
#include "inputs.h"
|
|
||||||
#include "utils.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "draw.h"
|
|
||||||
#include "bigclock.h"
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <time.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);
|
|
||||||
|
|
||||||
uint16_t len_login = strlen(lang.login);
|
|
||||||
uint16_t 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__) || defined(__FreeBSD__)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
static void doom_free(struct term_buf* buf);
|
|
||||||
static void matrix_free(struct term_buf* buf);
|
|
||||||
|
|
||||||
void draw_free(struct term_buf* buf)
|
|
||||||
{
|
|
||||||
if (config.animate)
|
|
||||||
{
|
|
||||||
switch (config.animation)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
doom_free(buf);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
matrix_free(buf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_box(struct term_buf* buf)
|
|
||||||
{
|
|
||||||
uint16_t box_x = (buf->width - buf->box_width) / 2;
|
|
||||||
uint16_t box_y = (buf->height - buf->box_height) / 2;
|
|
||||||
uint16_t box_x2 = (buf->width + buf->box_width) / 2;
|
|
||||||
uint16_t 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 (uint16_t 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 (uint16_t 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 (uint16_t i = 0; i < buf->box_height; ++i)
|
|
||||||
{
|
|
||||||
for (uint16_t k = 0; k < buf->box_width; ++k)
|
|
||||||
{
|
|
||||||
tb_put_cell(
|
|
||||||
box_x + k,
|
|
||||||
box_y + i,
|
|
||||||
&blank);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char* time_str(char* fmt, int maxlen)
|
|
||||||
{
|
|
||||||
time_t timer;
|
|
||||||
char* buffer = malloc(maxlen);
|
|
||||||
struct tm* tm_info;
|
|
||||||
|
|
||||||
timer = time(NULL);
|
|
||||||
tm_info = localtime(&timer);
|
|
||||||
|
|
||||||
if (strftime(buffer, maxlen, fmt, tm_info) == 0)
|
|
||||||
{
|
|
||||||
buffer[0] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern inline uint32_t* CLOCK_N(char c);
|
|
||||||
|
|
||||||
struct tb_cell* clock_cell(char c)
|
|
||||||
{
|
|
||||||
struct tb_cell* cells = malloc(sizeof(struct tb_cell) * CLOCK_W * CLOCK_H);
|
|
||||||
|
|
||||||
struct timeval tv;
|
|
||||||
gettimeofday(&tv, NULL);
|
|
||||||
if (config.animate && c == ':' && tv.tv_usec / 500000)
|
|
||||||
{
|
|
||||||
c = ' ';
|
|
||||||
}
|
|
||||||
uint32_t* clockchars = CLOCK_N(c);
|
|
||||||
|
|
||||||
for (int i = 0; i < CLOCK_W * CLOCK_H; i++)
|
|
||||||
{
|
|
||||||
cells[i].ch = clockchars[i];
|
|
||||||
cells[i].fg = config.fg;
|
|
||||||
cells[i].bg = config.bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cells;
|
|
||||||
}
|
|
||||||
|
|
||||||
void alpha_blit(struct tb_cell* buf, uint16_t x, uint16_t y, uint16_t w, uint16_t h, struct tb_cell* cells)
|
|
||||||
{
|
|
||||||
if (x + w >= tb_width() || y + h >= tb_height())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (int i = 0; i < h; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < w; j++)
|
|
||||||
{
|
|
||||||
struct tb_cell cell = cells[i * w + j];
|
|
||||||
if (cell.ch)
|
|
||||||
{
|
|
||||||
buf[(y + i) * tb_width() + (x + j)] = cell;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_bigclock(struct term_buf* buf)
|
|
||||||
{
|
|
||||||
if (!config.bigclock)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int xo = buf->width / 2 - (5 * (CLOCK_W + 1)) / 2;
|
|
||||||
int yo = (buf->height - buf->box_height) / 2 - CLOCK_H - 2;
|
|
||||||
|
|
||||||
char* clockstr = time_str("%H:%M", 6);
|
|
||||||
struct tb_cell* clockcell;
|
|
||||||
|
|
||||||
for (int i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
clockcell = clock_cell(clockstr[i]);
|
|
||||||
alpha_blit(tb_cell_buffer(), xo + i * (CLOCK_W + 1), yo, CLOCK_W, CLOCK_H, clockcell);
|
|
||||||
free(clockcell);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(clockstr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_clock(struct term_buf* buf)
|
|
||||||
{
|
|
||||||
if (config.clock == NULL || strlen(config.clock) == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* clockstr = time_str(config.clock, 32);
|
|
||||||
int clockstrlen = strlen(clockstr);
|
|
||||||
|
|
||||||
struct tb_cell* cells = strn_cell(clockstr, clockstrlen);
|
|
||||||
tb_blit(buf->width - clockstrlen, 0, clockstrlen, 1, cells);
|
|
||||||
|
|
||||||
free(clockstr);
|
|
||||||
free(cells);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct tb_cell* strn_cell(char* s, uint16_t len) // throws
|
|
||||||
{
|
|
||||||
struct tb_cell* cells = malloc((sizeof (struct tb_cell)) * len);
|
|
||||||
char* s2 = s;
|
|
||||||
uint32_t c;
|
|
||||||
|
|
||||||
if (cells != NULL)
|
|
||||||
{
|
|
||||||
for (uint16_t 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)
|
|
||||||
{
|
|
||||||
uint16_t 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
|
|
||||||
uint16_t 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)
|
|
||||||
{
|
|
||||||
uint16_t 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 (uint16_t 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)
|
|
||||||
{
|
|
||||||
uint16_t len = strlen(input->text);
|
|
||||||
uint16_t 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 (uint16_t 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)
|
|
||||||
{
|
|
||||||
uint16_t len = strlen(input->text);
|
|
||||||
uint16_t 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 (uint16_t 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)
|
|
||||||
{
|
|
||||||
uint16_t x = buf->box_x + config.margin_box_h + buf->labels_max_len + 1;
|
|
||||||
int32_t 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;
|
|
||||||
buf->astate.doom = malloc(sizeof(struct doom_state));
|
|
||||||
|
|
||||||
if (buf->astate.doom == NULL)
|
|
||||||
{
|
|
||||||
dgn_throw(DGN_ALLOC);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t tmp_len = buf->width * buf->height;
|
|
||||||
buf->astate.doom->buf = malloc(tmp_len);
|
|
||||||
tmp_len -= buf->width;
|
|
||||||
|
|
||||||
if (buf->astate.doom->buf == NULL)
|
|
||||||
{
|
|
||||||
dgn_throw(DGN_ALLOC);
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(buf->astate.doom->buf, 0, tmp_len);
|
|
||||||
memset(buf->astate.doom->buf + tmp_len, DOOM_STEPS - 1, buf->width);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void doom_free(struct term_buf* buf)
|
|
||||||
{
|
|
||||||
free(buf->astate.doom->buf);
|
|
||||||
free(buf->astate.doom);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adapted from cmatrix
|
|
||||||
static void matrix_init(struct term_buf* buf)
|
|
||||||
{
|
|
||||||
buf->init_width = buf->width;
|
|
||||||
buf->init_height = buf->height;
|
|
||||||
buf->astate.matrix = malloc(sizeof(struct matrix_state));
|
|
||||||
struct matrix_state* s = buf->astate.matrix;
|
|
||||||
|
|
||||||
if (s == NULL)
|
|
||||||
{
|
|
||||||
dgn_throw(DGN_ALLOC);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t len = buf->height + 1;
|
|
||||||
s->grid = malloc(sizeof(struct matrix_dot*) * len);
|
|
||||||
|
|
||||||
if (s->grid == NULL)
|
|
||||||
{
|
|
||||||
dgn_throw(DGN_ALLOC);
|
|
||||||
}
|
|
||||||
|
|
||||||
len = (buf->height + 1) * buf->width;
|
|
||||||
(s->grid)[0] = malloc(sizeof(struct matrix_dot) * len);
|
|
||||||
|
|
||||||
if ((s->grid)[0] == NULL)
|
|
||||||
{
|
|
||||||
dgn_throw(DGN_ALLOC);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 1; i <= buf->height; ++i)
|
|
||||||
{
|
|
||||||
s->grid[i] = s->grid[i - 1] + buf->width;
|
|
||||||
|
|
||||||
if (s->grid[i] == NULL)
|
|
||||||
{
|
|
||||||
dgn_throw(DGN_ALLOC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s->length = malloc(buf->width * sizeof(int));
|
|
||||||
|
|
||||||
if (s->length == NULL)
|
|
||||||
{
|
|
||||||
dgn_throw(DGN_ALLOC);
|
|
||||||
}
|
|
||||||
|
|
||||||
s->spaces = malloc(buf->width * sizeof(int));
|
|
||||||
|
|
||||||
if (s->spaces == NULL)
|
|
||||||
{
|
|
||||||
dgn_throw(DGN_ALLOC);
|
|
||||||
}
|
|
||||||
|
|
||||||
s->updates = malloc(buf->width * sizeof(int));
|
|
||||||
|
|
||||||
if (s->updates == NULL)
|
|
||||||
{
|
|
||||||
dgn_throw(DGN_ALLOC);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize grid
|
|
||||||
for (int i = 0; i <= buf->height; ++i)
|
|
||||||
{
|
|
||||||
for (int j = 0; j <= buf->width - 1; j += 2)
|
|
||||||
{
|
|
||||||
s->grid[i][j].val = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = 0; j < buf->width; j += 2)
|
|
||||||
{
|
|
||||||
s->spaces[j] = (int) rand() % buf->height + 1;
|
|
||||||
s->length[j] = (int) rand() % (buf->height - 3) + 3;
|
|
||||||
s->grid[1][j].val = ' ';
|
|
||||||
s->updates[j] = (int) rand() % 3 + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void matrix_free(struct term_buf* buf)
|
|
||||||
{
|
|
||||||
free(buf->astate.matrix->grid[0]);
|
|
||||||
free(buf->astate.matrix->grid);
|
|
||||||
free(buf->astate.matrix->length);
|
|
||||||
free(buf->astate.matrix->spaces);
|
|
||||||
free(buf->astate.matrix->updates);
|
|
||||||
free(buf->astate.matrix);
|
|
||||||
}
|
|
||||||
|
|
||||||
void animate_init(struct term_buf* buf)
|
|
||||||
{
|
|
||||||
if (config.animate)
|
|
||||||
{
|
|
||||||
switch(config.animation)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
{
|
|
||||||
doom_init(buf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
{
|
|
||||||
matrix_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
|
|
||||||
};
|
|
||||||
|
|
||||||
uint16_t src;
|
|
||||||
uint16_t random;
|
|
||||||
uint16_t dst;
|
|
||||||
|
|
||||||
uint16_t w = term_buf->init_width;
|
|
||||||
uint8_t* tmp = term_buf->astate.doom->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 (uint16_t x = 0; x < w; ++x)
|
|
||||||
{
|
|
||||||
for (uint16_t 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]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adapted from cmatrix
|
|
||||||
static void matrix(struct term_buf* buf)
|
|
||||||
{
|
|
||||||
static int frame = 3;
|
|
||||||
const int frame_delay = 8;
|
|
||||||
static int count = 0;
|
|
||||||
bool first_col;
|
|
||||||
struct matrix_state* s = buf->astate.matrix;
|
|
||||||
|
|
||||||
// Allowed codepoints
|
|
||||||
const int randmin = 33;
|
|
||||||
const int randnum = 123 - randmin;
|
|
||||||
// Chars change mid-scroll
|
|
||||||
const bool changes = true;
|
|
||||||
|
|
||||||
if ((buf->width != buf->init_width) || (buf->height != buf->init_height))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
count += 1;
|
|
||||||
if (count > frame_delay)
|
|
||||||
{
|
|
||||||
frame += 1;
|
|
||||||
if (frame > 4) frame = 1;
|
|
||||||
count = 0;
|
|
||||||
|
|
||||||
for (int j = 0; j < buf->width; j += 2)
|
|
||||||
{
|
|
||||||
int tail;
|
|
||||||
if (frame > s->updates[j])
|
|
||||||
{
|
|
||||||
if (s->grid[0][j].val == -1 && s->grid[1][j].val == ' ')
|
|
||||||
{
|
|
||||||
if (s->spaces[j] > 0)
|
|
||||||
{
|
|
||||||
s->spaces[j]--;
|
|
||||||
} else {
|
|
||||||
s->length[j] = (int) rand() % (buf->height - 3) + 3;
|
|
||||||
s->grid[0][j].val = (int) rand() % randnum + randmin;
|
|
||||||
s->spaces[j] = (int) rand() % buf->height + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int i = 0, seg_len = 0;
|
|
||||||
first_col = 1;
|
|
||||||
while (i <= buf->height)
|
|
||||||
{
|
|
||||||
// Skip over spaces
|
|
||||||
while (i <= buf->height
|
|
||||||
&& (s->grid[i][j].val == ' ' || s->grid[i][j].val == -1))
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i > buf->height) break;
|
|
||||||
|
|
||||||
// Find the head of this col
|
|
||||||
tail = i;
|
|
||||||
seg_len = 0;
|
|
||||||
while (i <= buf->height
|
|
||||||
&& (s->grid[i][j].val != ' ' && s->grid[i][j].val != -1))
|
|
||||||
{
|
|
||||||
s->grid[i][j].is_head = false;
|
|
||||||
if (changes)
|
|
||||||
{
|
|
||||||
if (rand() % 8 == 0)
|
|
||||||
s->grid[i][j].val = (int) rand() % randnum + randmin;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
seg_len++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Head's down offscreen
|
|
||||||
if (i > buf->height)
|
|
||||||
{
|
|
||||||
s->grid[tail][j].val = ' ';
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
s->grid[i][j].val = (int) rand() % randnum + randmin;
|
|
||||||
s->grid[i][j].is_head = true;
|
|
||||||
|
|
||||||
if (seg_len > s->length[j] || !first_col) {
|
|
||||||
s->grid[tail][j].val = ' ';
|
|
||||||
s->grid[0][j].val = -1;
|
|
||||||
}
|
|
||||||
first_col = 0;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t blank;
|
|
||||||
utf8_char_to_unicode(&blank, " ");
|
|
||||||
|
|
||||||
for (int j = 0; j < buf->width; j += 2)
|
|
||||||
{
|
|
||||||
for (int i = 1; i <= buf->height; ++i)
|
|
||||||
{
|
|
||||||
uint32_t c;
|
|
||||||
int fg = TB_GREEN;
|
|
||||||
int bg = TB_DEFAULT;
|
|
||||||
|
|
||||||
if (s->grid[i][j].val == -1 || s->grid[i][j].val == ' ')
|
|
||||||
{
|
|
||||||
tb_change_cell(j, i - 1, blank, fg, bg);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
char tmp[2];
|
|
||||||
tmp[0] = s->grid[i][j].val;
|
|
||||||
tmp[1] = '\0';
|
|
||||||
if(utf8_char_to_unicode(&c, tmp))
|
|
||||||
{
|
|
||||||
if (s->grid[i][j].is_head)
|
|
||||||
{
|
|
||||||
fg = TB_WHITE | TB_BOLD;
|
|
||||||
}
|
|
||||||
tb_change_cell(j, i - 1, c, fg, bg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void animate(struct term_buf* buf)
|
|
||||||
{
|
|
||||||
buf->width = tb_width();
|
|
||||||
buf->height = tb_height();
|
|
||||||
|
|
||||||
if (config.animate)
|
|
||||||
{
|
|
||||||
switch(config.animation)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
{
|
|
||||||
doom(buf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
{
|
|
||||||
matrix(buf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cascade(struct term_buf* term_buf, uint8_t* fails)
|
|
||||||
{
|
|
||||||
uint16_t width = term_buf->width;
|
|
||||||
uint16_t 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;
|
|
||||||
}
|
|
||||||
92
src/draw.h
92
src/draw.h
@@ -1,92 +0,0 @@
|
|||||||
#ifndef H_LY_DRAW
|
|
||||||
#define H_LY_DRAW
|
|
||||||
|
|
||||||
#include "termbox.h"
|
|
||||||
#include "inputs.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
struct box
|
|
||||||
{
|
|
||||||
uint32_t left_up;
|
|
||||||
uint32_t left_down;
|
|
||||||
uint32_t right_up;
|
|
||||||
uint32_t right_down;
|
|
||||||
uint32_t top;
|
|
||||||
uint32_t bot;
|
|
||||||
uint32_t left;
|
|
||||||
uint32_t right;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct matrix_dot
|
|
||||||
{
|
|
||||||
int val;
|
|
||||||
bool is_head;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct matrix_state
|
|
||||||
{
|
|
||||||
struct matrix_dot** grid;
|
|
||||||
int* length;
|
|
||||||
int* spaces;
|
|
||||||
int* updates;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct doom_state
|
|
||||||
{
|
|
||||||
uint8_t* buf;
|
|
||||||
};
|
|
||||||
|
|
||||||
union anim_state
|
|
||||||
{
|
|
||||||
struct doom_state* doom;
|
|
||||||
struct matrix_state* matrix;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct term_buf
|
|
||||||
{
|
|
||||||
uint16_t width;
|
|
||||||
uint16_t height;
|
|
||||||
uint16_t init_width;
|
|
||||||
uint16_t init_height;
|
|
||||||
|
|
||||||
struct box box_chars;
|
|
||||||
char* info_line;
|
|
||||||
uint16_t labels_max_len;
|
|
||||||
uint16_t box_x;
|
|
||||||
uint16_t box_y;
|
|
||||||
uint16_t box_width;
|
|
||||||
uint16_t box_height;
|
|
||||||
|
|
||||||
union anim_state astate;
|
|
||||||
};
|
|
||||||
|
|
||||||
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, uint16_t 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, uint8_t* fails);
|
|
||||||
|
|
||||||
void draw_bigclock(struct term_buf *buf);
|
|
||||||
void draw_clock(struct term_buf *buf);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
18
src/enums.zig
Normal file
18
src/enums.zig
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
pub const Animation = enum {
|
||||||
|
none,
|
||||||
|
doom,
|
||||||
|
matrix,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DisplayServer = enum {
|
||||||
|
wayland,
|
||||||
|
shell,
|
||||||
|
xinitrc,
|
||||||
|
x11,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Input = enum {
|
||||||
|
session,
|
||||||
|
login,
|
||||||
|
password,
|
||||||
|
};
|
||||||
285
src/inputs.c
285
src/inputs.c
@@ -1,285 +0,0 @@
|
|||||||
#include "dragonfail.h"
|
|
||||||
#include "termbox.h"
|
|
||||||
#include "inputs.h"
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <ctype.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 || (event->key == TB_KEY_CTRL_H))
|
|
||||||
{
|
|
||||||
input_desktop_right(target);
|
|
||||||
}
|
|
||||||
else if (event->key == TB_KEY_ARROW_RIGHT || (event->key == TB_KEY_CTRL_L))
|
|
||||||
{
|
|
||||||
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->list_simple = 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(config.xinitrc), DS_XINITRC);
|
|
||||||
#if 0
|
|
||||||
input_desktop_add(target, strdup(lang.wayland), strdup(""), DS_WAYLAND);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void input_text(struct text* target, uint64_t 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 (uint16_t 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->list_simple = realloc(target->list_simple, 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;
|
|
||||||
|
|
||||||
int name_len = strlen(name);
|
|
||||||
char* name_simple = strdup(name);
|
|
||||||
|
|
||||||
if (strstr(name_simple, " ") != NULL)
|
|
||||||
{
|
|
||||||
name_simple = strtok(name_simple, " ");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < name_len; i++)
|
|
||||||
{
|
|
||||||
name_simple[i] = tolower(name_simple[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
target->list_simple[target->cur] = name_simple;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
57
src/inputs.h
57
src/inputs.h
@@ -1,57 +0,0 @@
|
|||||||
#ifndef H_LY_INPUTS
|
|
||||||
#define H_LY_INPUTS
|
|
||||||
|
|
||||||
#include "termbox.h"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
enum display_server {DS_WAYLAND, DS_SHELL, DS_XINITRC, DS_XORG};
|
|
||||||
|
|
||||||
struct text
|
|
||||||
{
|
|
||||||
char* text;
|
|
||||||
char* end;
|
|
||||||
int64_t len;
|
|
||||||
char* cur;
|
|
||||||
char* visible_start;
|
|
||||||
uint16_t visible_len;
|
|
||||||
|
|
||||||
uint16_t x;
|
|
||||||
uint16_t y;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct desktop
|
|
||||||
{
|
|
||||||
char** list;
|
|
||||||
char** list_simple;
|
|
||||||
char** cmd;
|
|
||||||
enum display_server* display_server;
|
|
||||||
|
|
||||||
uint16_t cur;
|
|
||||||
uint16_t len;
|
|
||||||
uint16_t visible_len;
|
|
||||||
uint16_t x;
|
|
||||||
uint16_t 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, uint64_t 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
|
|
||||||
119
src/interop.zig
Normal file
119
src/interop.zig
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
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("utmp.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
pub const xcb = @cImport({
|
||||||
|
@cInclude("xcb/xcb.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
pub const c_size = u64;
|
||||||
|
pub const c_uid = u32;
|
||||||
|
pub const c_gid = u32;
|
||||||
|
pub const c_time = c_long;
|
||||||
|
pub const tm = extern struct {
|
||||||
|
tm_sec: c_int,
|
||||||
|
tm_min: c_int,
|
||||||
|
tm_hour: c_int,
|
||||||
|
tm_mday: c_int,
|
||||||
|
tm_mon: c_int,
|
||||||
|
tm_year: c_int,
|
||||||
|
tm_wday: c_int,
|
||||||
|
tm_yday: c_int,
|
||||||
|
tm_isdst: c_int,
|
||||||
|
};
|
||||||
|
pub const passwd = extern struct {
|
||||||
|
pw_name: [*:0]u8,
|
||||||
|
pw_passwd: [*:0]u8,
|
||||||
|
|
||||||
|
pw_uid: c_uid,
|
||||||
|
pw_gid: c_gid,
|
||||||
|
pw_gecos: [*:0]u8,
|
||||||
|
pw_dir: [*:0]u8,
|
||||||
|
pw_shell: [*:0]u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const VT_ACTIVATE: c_int = 0x5606;
|
||||||
|
pub const VT_WAITACTIVE: c_int = 0x5607;
|
||||||
|
|
||||||
|
pub const KDGETLED: c_int = 0x4B31;
|
||||||
|
pub const KDSETLED: c_int = 0x4B32;
|
||||||
|
pub const KDGKBLED: c_int = 0x4B64;
|
||||||
|
pub const KDSKBLED: c_int = 0x4B65;
|
||||||
|
|
||||||
|
pub const LED_NUM: c_int = 0x02;
|
||||||
|
pub const LED_CAP: c_int = 0x04;
|
||||||
|
|
||||||
|
pub const K_NUMLOCK: c_int = 0x02;
|
||||||
|
pub const K_CAPSLOCK: c_int = 0x04;
|
||||||
|
|
||||||
|
pub extern "c" fn localtime(timer: *const c_time) *tm;
|
||||||
|
pub extern "c" fn strftime(str: [*:0]u8, maxsize: c_size, format: [*:0]const u8, timeptr: *const tm) c_size;
|
||||||
|
pub extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int;
|
||||||
|
pub extern "c" fn putenv(name: [*:0]u8) c_int;
|
||||||
|
pub extern "c" fn getuid() c_uid;
|
||||||
|
pub extern "c" fn getpwnam(name: [*:0]const u8) ?*passwd;
|
||||||
|
pub extern "c" fn endpwent() void;
|
||||||
|
pub extern "c" fn setusershell() void;
|
||||||
|
pub extern "c" fn getusershell() [*:0]u8;
|
||||||
|
pub extern "c" fn endusershell() void;
|
||||||
|
pub extern "c" fn initgroups(user: [*:0]const u8, group: c_gid) c_int;
|
||||||
|
|
||||||
|
pub fn timeAsString(buf: [:0]u8, format: [:0]const u8) ![]u8 {
|
||||||
|
const timer = std.time.timestamp();
|
||||||
|
const tm_info = localtime(&timer);
|
||||||
|
|
||||||
|
const len = strftime(buf, buf.len, format, tm_info);
|
||||||
|
if (len < 0) return error.CannotGetFormattedTime;
|
||||||
|
|
||||||
|
return buf[0..len];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getLockState(console_dev: [:0]const u8) !struct {
|
||||||
|
numlock: bool,
|
||||||
|
capslock: bool,
|
||||||
|
} {
|
||||||
|
const fd = std.c.open(console_dev, .{ .ACCMODE = .RDONLY });
|
||||||
|
if (fd < 0) return error.CannotOpenConsoleDev;
|
||||||
|
defer _ = std.c.close(fd);
|
||||||
|
|
||||||
|
var numlock = false;
|
||||||
|
var capslock = false;
|
||||||
|
|
||||||
|
if (builtin.os.tag.isBSD()) {
|
||||||
|
var led: c_int = undefined;
|
||||||
|
_ = std.c.ioctl(fd, KDGETLED, &led);
|
||||||
|
numlock = (led & LED_NUM) != 0;
|
||||||
|
capslock = (led & LED_CAP) != 0;
|
||||||
|
} else {
|
||||||
|
var led: c_char = undefined;
|
||||||
|
_ = std.c.ioctl(fd, KDGKBLED, &led);
|
||||||
|
numlock = (led & K_NUMLOCK) != 0;
|
||||||
|
capslock = (led & K_CAPSLOCK) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.numlock = numlock,
|
||||||
|
.capslock = capslock,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setNumlock(val: bool) !void {
|
||||||
|
var led: c_char = undefined;
|
||||||
|
_ = std.c.ioctl(0, KDGKBLED, &led);
|
||||||
|
|
||||||
|
const numlock = (led & K_NUMLOCK) != 0;
|
||||||
|
if (numlock != val) {
|
||||||
|
const status = std.c.ioctl(std.posix.STDIN_FILENO, KDSKBLED, led ^ K_NUMLOCK);
|
||||||
|
if (status != 0) return error.FailedToSetNumlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
715
src/login.c
715
src/login.c
@@ -1,715 +0,0 @@
|
|||||||
#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 <stdint.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];
|
|
||||||
uint8_t 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;
|
|
||||||
|
|
||||||
setenv("TERM", term ? 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 ? lang : "C", 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_session(const enum display_server display_server)
|
|
||||||
{
|
|
||||||
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 env_xdg(const char* tty_id, const char* desktop_name)
|
|
||||||
{
|
|
||||||
char user[20];
|
|
||||||
snprintf(user, 20, "/run/user/%d", getuid());
|
|
||||||
setenv("XDG_RUNTIME_DIR", user, 0);
|
|
||||||
setenv("XDG_SESSION_CLASS", "user", 0);
|
|
||||||
setenv("XDG_SESSION_ID", "1", 0);
|
|
||||||
setenv("XDG_SESSION_DESKTOP", desktop_name, 0);
|
|
||||||
setenv("XDG_SEAT", "seat0", 0);
|
|
||||||
setenv("XDG_VTNR", tty_id, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
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, char* pwd)
|
|
||||||
{
|
|
||||||
const char* xauth_file = "lyxauth";
|
|
||||||
char* xauth_dir = getenv("XDG_RUNTIME_DIR");
|
|
||||||
if ((xauth_dir == NULL) || (*xauth_dir == '\0'))
|
|
||||||
{
|
|
||||||
xauth_dir = getenv("XDG_CONFIG_HOME");
|
|
||||||
struct stat sb;
|
|
||||||
if ((xauth_dir == NULL) || (*xauth_dir == '\0'))
|
|
||||||
{
|
|
||||||
xauth_dir = strdup(pwd);
|
|
||||||
strcat(xauth_dir, "/.config");
|
|
||||||
stat(xauth_dir, &sb);
|
|
||||||
if (S_ISDIR(sb.st_mode))
|
|
||||||
{
|
|
||||||
strcat(xauth_dir, "/ly");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xauth_dir = pwd;
|
|
||||||
xauth_file = ".lyxauth";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
strcat(xauth_dir, "/ly");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If .config/ly/ or XDG_CONFIG_HOME/ly/ doesn't exist and can't create the directory, use pwd
|
|
||||||
// Passing pwd beforehand is safe since stat will always evaluate false
|
|
||||||
stat(xauth_dir, &sb);
|
|
||||||
if (!S_ISDIR(sb.st_mode) && mkdir(xauth_dir, 0777) == -1)
|
|
||||||
{
|
|
||||||
xauth_dir = pwd;
|
|
||||||
xauth_file = ".lyxauth";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// trim trailing slashes
|
|
||||||
int i = strlen(xauth_dir) - 1;
|
|
||||||
while (xauth_dir[i] == '/') i--;
|
|
||||||
xauth_dir[i + 1] = '\0';
|
|
||||||
|
|
||||||
char xauthority[256];
|
|
||||||
snprintf(xauthority, 256, "%s/%s", xauth_dir, xauth_file);
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
char display_name[4];
|
|
||||||
|
|
||||||
snprintf(display_name, 3, ":%d", get_free_display());
|
|
||||||
xauth(display_name, pwd->pw_shell, pwd->pw_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;
|
|
||||||
|
|
||||||
char tty_id [3];
|
|
||||||
snprintf(tty_id, 3, "%d", config.tty);
|
|
||||||
|
|
||||||
// Add XDG environment variables
|
|
||||||
env_xdg_session(desktop->display_server[desktop->cur]);
|
|
||||||
env_xdg(tty_id, desktop->list_simple[desktop->cur]);
|
|
||||||
|
|
||||||
// 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 vt[5];
|
|
||||||
snprintf(vt, 5, "vt%d", config.tty);
|
|
||||||
|
|
||||||
// set env (this clears the environment)
|
|
||||||
env_init(pwd);
|
|
||||||
// Re-add XDG environment variables from lines 508,509
|
|
||||||
env_xdg_session(desktop->display_server[desktop->cur]);
|
|
||||||
env_xdg(tty_id, desktop->list_simple[desktop->cur]);
|
|
||||||
|
|
||||||
if (dgn_catch())
|
|
||||||
{
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add pam variables
|
|
||||||
char** env = pam_getenvlist(handle);
|
|
||||||
|
|
||||||
for (uint16_t i = 0; env && env[i]; ++i)
|
|
||||||
{
|
|
||||||
putenv(env[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
13
src/login.h
@@ -1,13 +0,0 @@
|
|||||||
#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
|
|
||||||
356
src/main.c
356
src/main.c
@@ -1,356 +0,0 @@
|
|||||||
#include "argoat.h"
|
|
||||||
#include "configator.h"
|
|
||||||
#include "dragonfail.h"
|
|
||||||
#include "termbox.h"
|
|
||||||
|
|
||||||
#include "draw.h"
|
|
||||||
#include "inputs.h"
|
|
||||||
#include "login.h"
|
|
||||||
#include "utils.h"
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#define ARG_COUNT 7
|
|
||||||
|
|
||||||
#ifndef LY_VERSION
|
|
||||||
#define LY_VERSION "0.6.0"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// global
|
|
||||||
struct lang lang;
|
|
||||||
struct config config;
|
|
||||||
|
|
||||||
// args handles
|
|
||||||
void arg_help(void* data, char** pars, const int pars_count)
|
|
||||||
{
|
|
||||||
printf("If you want to configure Ly, please check the config file, usually located at /etc/ly/config.ini.\n");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void arg_version(void* data, char** pars, const int pars_count)
|
|
||||||
{
|
|
||||||
printf("Ly version %s\n", LY_VERSION);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
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;
|
|
||||||
|
|
||||||
//Place the curser on the login field if there is no saved username, if there is, place the curser on the password field
|
|
||||||
uint8_t active_input;
|
|
||||||
if (config.default_input == LOGIN_INPUT && login.text != login.end){
|
|
||||||
active_input = PASSWORD_INPUT;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
active_input = config.default_input;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// init drawing stuff
|
|
||||||
draw_init(&buf);
|
|
||||||
|
|
||||||
// draw_box and position_input are called because they need to be
|
|
||||||
// called before *input_handles[active_input] for the cursor to be
|
|
||||||
// positioned correctly
|
|
||||||
draw_box(&buf);
|
|
||||||
position_input(&buf, &desktop, &login, &password);
|
|
||||||
(*input_handles[active_input])(input_structs[active_input], NULL);
|
|
||||||
|
|
||||||
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;
|
|
||||||
uint8_t auth_fails = 0;
|
|
||||||
|
|
||||||
switch_tty(&buf);
|
|
||||||
|
|
||||||
// main loop
|
|
||||||
while (run)
|
|
||||||
{
|
|
||||||
if (update)
|
|
||||||
{
|
|
||||||
if (auth_fails < 10)
|
|
||||||
{
|
|
||||||
(*input_handles[active_input])(input_structs[active_input], NULL);
|
|
||||||
tb_clear();
|
|
||||||
animate(&buf);
|
|
||||||
draw_bigclock(&buf);
|
|
||||||
draw_box(&buf);
|
|
||||||
draw_clock(&buf);
|
|
||||||
draw_labels(&buf);
|
|
||||||
if(!config.hide_f1_commands)
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
int timeout = -1;
|
|
||||||
|
|
||||||
if (config.animate)
|
|
||||||
{
|
|
||||||
timeout = config.min_refresh_delta;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct timeval tv;
|
|
||||||
gettimeofday(&tv, NULL);
|
|
||||||
if (config.bigclock)
|
|
||||||
timeout = (60 - tv.tv_sec % 60) * 1000 - tv.tv_usec / 1000 + 1;
|
|
||||||
if (config.clock)
|
|
||||||
timeout = 1000 - tv.tv_usec / 1000 + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeout == -1)
|
|
||||||
{
|
|
||||||
error = tb_poll_event(&event);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
error = tb_peek_event(&event, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
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]);
|
|
||||||
update = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TB_KEY_CTRL_K:
|
|
||||||
case TB_KEY_ARROW_UP:
|
|
||||||
if (active_input > 0)
|
|
||||||
{
|
|
||||||
--active_input;
|
|
||||||
update = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TB_KEY_CTRL_J:
|
|
||||||
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 = SESSION_SWITCH;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
else if (reboot)
|
|
||||||
{
|
|
||||||
execl("/bin/sh", "sh", "-c", config.restart_cmd, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
config_free();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
695
src/main.zig
Normal file
695
src/main.zig
Normal file
@@ -0,0 +1,695 @@
|
|||||||
|
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 Desktop = @import("tui/components/Desktop.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 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
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 info_line = InfoLine{};
|
||||||
|
|
||||||
|
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 usually located at /etc/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.data_directory ++ "/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{};
|
||||||
|
|
||||||
|
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, config.save_file);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const config_path = build_options.data_directory ++ "/config.ini";
|
||||||
|
|
||||||
|
config = config_ini.readFileToStruct(config_path, comment_characters, migrator.configFieldHandler) catch Config{};
|
||||||
|
|
||||||
|
const lang_path = try std.fmt.allocPrint(allocator, "{s}/lang/{s}.ini", .{ build_options.data_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, config.save_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
interop.setNumlock(config.numlock) catch {};
|
||||||
|
|
||||||
|
if (config.initial_info_text) |text| {
|
||||||
|
try info_line.setText(text);
|
||||||
|
} else get_host_name: {
|
||||||
|
// Initialize information line with host name
|
||||||
|
var name_buf: [std.posix.HOST_NAME_MAX]u8 = undefined;
|
||||||
|
const hostname = std.posix.gethostname(&name_buf) catch {
|
||||||
|
try info_line.setText(lang.err_hostname);
|
||||||
|
break :get_host_name;
|
||||||
|
};
|
||||||
|
try info_line.setText(hostname);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize termbox
|
||||||
|
_ = 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);
|
||||||
|
|
||||||
|
// Get a random seed for the PRNG (used by animations)
|
||||||
|
var seed: u64 = undefined;
|
||||||
|
try std.posix.getrandom(std.mem.asBytes(&seed));
|
||||||
|
|
||||||
|
var prng = std.Random.DefaultPrng.init(seed);
|
||||||
|
const random = prng.random();
|
||||||
|
|
||||||
|
var buffer = TerminalBuffer.init(config, labels_max_length, random);
|
||||||
|
|
||||||
|
// Initialize components
|
||||||
|
var desktop = try Desktop.init(allocator, &buffer, config.max_desktop_len, lang);
|
||||||
|
defer desktop.deinit();
|
||||||
|
|
||||||
|
desktop.addEnvironment(.{ .Name = lang.shell }, "", .shell) catch {
|
||||||
|
try info_line.setText(lang.err_alloc);
|
||||||
|
};
|
||||||
|
if (config.xinitrc) |xinitrc| {
|
||||||
|
desktop.addEnvironment(.{ .Name = lang.xinitrc, .Exec = xinitrc }, "", .xinitrc) catch {
|
||||||
|
try info_line.setText(lang.err_alloc);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try desktop.crawl(config.waylandsessions, .wayland);
|
||||||
|
try desktop.crawl(config.xsessions, .x11);
|
||||||
|
|
||||||
|
var login = try Text.init(allocator, &buffer, config.max_login_len);
|
||||||
|
defer login.deinit();
|
||||||
|
|
||||||
|
var password = try Text.init(allocator, &buffer, config.max_password_len);
|
||||||
|
defer password.deinit();
|
||||||
|
|
||||||
|
var active_input = config.default_input;
|
||||||
|
var insert_mode = !config.vi_mode;
|
||||||
|
|
||||||
|
// 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 < desktop.environments.items.len) desktop.current = session_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Place components on the screen
|
||||||
|
{
|
||||||
|
buffer.drawBoxCenter(!config.hide_borders, config.blank_box);
|
||||||
|
|
||||||
|
const coordinates = buffer.calculateComponentCoordinates();
|
||||||
|
desktop.position(coordinates.x, coordinates.y + 2, coordinates.visible_length);
|
||||||
|
login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length);
|
||||||
|
password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length);
|
||||||
|
|
||||||
|
switch (active_input) {
|
||||||
|
.session => desktop.handle(null, insert_mode),
|
||||||
|
.login => login.handle(null, insert_mode) catch {
|
||||||
|
try info_line.setText(lang.err_alloc);
|
||||||
|
},
|
||||||
|
.password => password.handle(null, insert_mode) catch {
|
||||||
|
try info_line.setText(lang.err_alloc);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
|
var event: termbox.tb_event = undefined;
|
||||||
|
var run = true;
|
||||||
|
var update = true;
|
||||||
|
var resolution_changed = false;
|
||||||
|
var auth_fails: u64 = 0;
|
||||||
|
|
||||||
|
// Switch to selected TTY if possible
|
||||||
|
open_console_dev: {
|
||||||
|
const fd = std.posix.open(config.console_dev, .{ .ACCMODE = .WRONLY }, 0) catch {
|
||||||
|
try info_line.setText(lang.err_console_dev);
|
||||||
|
break :open_console_dev;
|
||||||
|
};
|
||||||
|
defer std.posix.close(fd);
|
||||||
|
|
||||||
|
_ = std.c.ioctl(fd, interop.VT_ACTIVATE, config.tty);
|
||||||
|
_ = std.c.ioctl(fd, interop.VT_WAITACTIVE, config.tty);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (run) {
|
||||||
|
// 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: u64 = @intCast(termbox.tb_width());
|
||||||
|
const height: u64 = @intCast(termbox.tb_height());
|
||||||
|
|
||||||
|
if (width != buffer.width) {
|
||||||
|
buffer.width = width;
|
||||||
|
resolution_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (height != buffer.height) {
|
||||||
|
buffer.height = height;
|
||||||
|
resolution_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it did change, then update the cell buffer, reallocate the current animation's buffers, and force a draw update
|
||||||
|
if (resolution_changed) {
|
||||||
|
buffer.buffer = termbox.tb_cell_buffer();
|
||||||
|
|
||||||
|
switch (config.animation) {
|
||||||
|
.none => {},
|
||||||
|
.doom => doom.realloc() catch {
|
||||||
|
try info_line.setText(lang.err_alloc);
|
||||||
|
},
|
||||||
|
.matrix => matrix.realloc() catch {
|
||||||
|
try info_line.setText(lang.err_alloc);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update) {
|
||||||
|
// If the user entered a wrong password 10 times in a row, play a cascade animation, else update normally
|
||||||
|
if (auth_fails < 10) {
|
||||||
|
_ = termbox.tb_clear();
|
||||||
|
|
||||||
|
switch (config.animation) {
|
||||||
|
.none => {},
|
||||||
|
.doom => doom.draw(),
|
||||||
|
.matrix => matrix.draw(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.bigclock 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);
|
||||||
|
bigclock.alphaBlit(buffer.buffer, 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();
|
||||||
|
desktop.position(coordinates.x, coordinates.y + 2, coordinates.visible_length);
|
||||||
|
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) {
|
||||||
|
.session => desktop.handle(null, insert_mode),
|
||||||
|
.login => login.handle(null, insert_mode) catch {
|
||||||
|
try info_line.setText(lang.err_alloc);
|
||||||
|
},
|
||||||
|
.password => password.handle(null, insert_mode) catch {
|
||||||
|
try info_line.setText(lang.err_alloc);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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.draw(buffer);
|
||||||
|
|
||||||
|
if (!config.hide_key_hints) {
|
||||||
|
var length: u64 = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.setText(lang.err_console_dev);
|
||||||
|
break :draw_lock_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
var lock_state_x = buffer.width - @min(buffer.width, lang.numlock.len);
|
||||||
|
const lock_state_y: u64 = if (config.clock != null) 1 else 0;
|
||||||
|
|
||||||
|
if (lock_state.numlock) buffer.drawLabel(lang.numlock, lock_state_x, lock_state_y);
|
||||||
|
|
||||||
|
if (lock_state_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
desktop.draw();
|
||||||
|
login.draw();
|
||||||
|
password.drawMasked(config.asterisk);
|
||||||
|
} 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: i32 = -1;
|
||||||
|
|
||||||
|
// Calculate the maximum timeout based on current animations, or the (big) clock. If there's none, we wait for the event indefinitely instead
|
||||||
|
if (animate) {
|
||||||
|
timeout = config.min_refresh_delta;
|
||||||
|
} else if (config.bigclock and config.clock == null) {
|
||||||
|
var tv: std.c.timeval = undefined;
|
||||||
|
_ = std.c.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 >= 10) {
|
||||||
|
var tv: std.c.timeval = undefined;
|
||||||
|
_ = std.c.gettimeofday(&tv, null);
|
||||||
|
|
||||||
|
timeout = @intCast(1000 - @divTrunc(tv.tv_usec, 1000) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const event_error = if (timeout == -1) termbox.tb_poll_event(&event) else termbox.tb_peek_event(&event, timeout);
|
||||||
|
|
||||||
|
update = timeout != -1;
|
||||||
|
|
||||||
|
if (event_error < 0 or event.type != termbox.TB_EVENT_KEY) continue;
|
||||||
|
|
||||||
|
switch (event.key) {
|
||||||
|
termbox.TB_KEY_ESC => {
|
||||||
|
if (config.vi_mode and insert_mode) {
|
||||||
|
insert_mode = false;
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
termbox.TB_KEY_F12...termbox.TB_KEY_F1 => {
|
||||||
|
const pressed_key = 0xFFFF - event.key + 1;
|
||||||
|
if (pressed_key == shutdown_key) {
|
||||||
|
shutdown = true;
|
||||||
|
run = false;
|
||||||
|
} else if (pressed_key == restart_key) {
|
||||||
|
restart = true;
|
||||||
|
run = false;
|
||||||
|
} else if (pressed_key == sleep_key) {
|
||||||
|
if (config.sleep_cmd) |sleep_cmd| {
|
||||||
|
var sleep = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", sleep_cmd }, allocator);
|
||||||
|
_ = sleep.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, .login => .session,
|
||||||
|
.password => .login,
|
||||||
|
};
|
||||||
|
update = true;
|
||||||
|
},
|
||||||
|
termbox.TB_KEY_CTRL_J, termbox.TB_KEY_ARROW_DOWN => {
|
||||||
|
active_input = switch (active_input) {
|
||||||
|
.session => .login,
|
||||||
|
.login, .password => .password,
|
||||||
|
};
|
||||||
|
update = true;
|
||||||
|
},
|
||||||
|
termbox.TB_KEY_TAB => {
|
||||||
|
active_input = switch (active_input) {
|
||||||
|
.session => .login,
|
||||||
|
.login => .password,
|
||||||
|
.password => .session,
|
||||||
|
};
|
||||||
|
update = true;
|
||||||
|
},
|
||||||
|
termbox.TB_KEY_BACK_TAB => {
|
||||||
|
active_input = switch (active_input) {
|
||||||
|
.session => .password,
|
||||||
|
.login => .session,
|
||||||
|
.password => .login,
|
||||||
|
};
|
||||||
|
|
||||||
|
update = true;
|
||||||
|
},
|
||||||
|
termbox.TB_KEY_ENTER => {
|
||||||
|
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 = desktop.current,
|
||||||
|
};
|
||||||
|
ini.writeFromStruct(save_data, file.writer(), null, true, .{}) catch break :save_last_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
try info_line.setText(lang.authenticating);
|
||||||
|
InfoLine.clearRendered(allocator, buffer) catch {};
|
||||||
|
info_line.draw(buffer);
|
||||||
|
_ = termbox.tb_present();
|
||||||
|
|
||||||
|
session_pid = try std.posix.fork();
|
||||||
|
if (session_pid == 0) {
|
||||||
|
const current_environment = desktop.environments.items[desktop.current];
|
||||||
|
auth.authenticate(config, current_environment, login_text, password_text) catch |err| {
|
||||||
|
shared_err.writeError(err);
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
std.process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = std.posix.waitpid(session_pid, 0);
|
||||||
|
session_pid = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auth_err = shared_err.readError();
|
||||||
|
if (auth_err) |err| {
|
||||||
|
auth_fails += 1;
|
||||||
|
active_input = .password;
|
||||||
|
try info_line.setText(getAuthErrorMsg(err, lang));
|
||||||
|
if (config.clear_password or err != error.PamAuthError) password.clear();
|
||||||
|
} else {
|
||||||
|
password.clear();
|
||||||
|
try info_line.setText(lang.logout);
|
||||||
|
}
|
||||||
|
|
||||||
|
try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios);
|
||||||
|
if (auth_fails < 10) {
|
||||||
|
_ = termbox.tb_clear();
|
||||||
|
_ = termbox.tb_present();
|
||||||
|
}
|
||||||
|
|
||||||
|
update = true;
|
||||||
|
|
||||||
|
var restore_cursor = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", config.term_restore_cursor_cmd }, allocator);
|
||||||
|
_ = restore_cursor.spawnAndWait() catch .{};
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
if (!insert_mode) {
|
||||||
|
switch (event.ch) {
|
||||||
|
'k' => {
|
||||||
|
active_input = switch (active_input) {
|
||||||
|
.session, .login => .session,
|
||||||
|
.password => .login,
|
||||||
|
};
|
||||||
|
update = true;
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
'j' => {
|
||||||
|
active_input = switch (active_input) {
|
||||||
|
.session => .login,
|
||||||
|
.login, .password => .password,
|
||||||
|
};
|
||||||
|
update = true;
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
'i' => {
|
||||||
|
insert_mode = true;
|
||||||
|
update = true;
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (active_input) {
|
||||||
|
.session => desktop.handle(&event, insert_mode),
|
||||||
|
.login => login.handle(&event, insert_mode) catch {
|
||||||
|
try info_line.setText(lang.err_alloc);
|
||||||
|
},
|
||||||
|
.password => password.handle(&event, insert_mode) catch {
|
||||||
|
try info_line.setText(lang.err_alloc);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
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.McookieFailed => lang.err_mcookie,
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
188
src/tui/TerminalBuffer.zig
Normal file
188
src/tui/TerminalBuffer.zig
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
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: u64,
|
||||||
|
height: u64,
|
||||||
|
buffer: [*]termbox.tb_cell,
|
||||||
|
fg: u8,
|
||||||
|
bg: u8,
|
||||||
|
border_fg: u8,
|
||||||
|
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: u64,
|
||||||
|
box_x: u64,
|
||||||
|
box_y: u64,
|
||||||
|
box_width: u64,
|
||||||
|
box_height: u64,
|
||||||
|
margin_box_v: u8,
|
||||||
|
margin_box_h: u8,
|
||||||
|
|
||||||
|
pub fn init(config: Config, labels_max_length: u64, 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 changes = false;
|
||||||
|
|
||||||
|
var y = self.height - 2;
|
||||||
|
while (y > 0) : (y -= 1) {
|
||||||
|
for (0..self.width) |x| {
|
||||||
|
const c: u8 = @truncate(self.buffer[(y - 1) * self.width + x].ch);
|
||||||
|
if (std.ascii.isWhitespace(c)) continue;
|
||||||
|
|
||||||
|
const c_under: u8 = @truncate(self.buffer[y * self.width + x].ch);
|
||||||
|
if (!std.ascii.isWhitespace(c_under)) continue;
|
||||||
|
|
||||||
|
changes = true;
|
||||||
|
|
||||||
|
if ((self.random.int(u16) % 10) > 7) continue;
|
||||||
|
|
||||||
|
self.buffer[y * self.width + x] = self.buffer[(y - 1) * self.width + x];
|
||||||
|
self.buffer[(y - 1) * self.width + x].ch = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(@intCast(x1 + i), @intCast(y1 - 1), &c1);
|
||||||
|
_ = utils.putCell(@intCast(x1 + i), @intCast(y2), &c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
c1.ch = self.box_chars.left;
|
||||||
|
c2.ch = self.box_chars.right;
|
||||||
|
|
||||||
|
for (0..self.box_height) |i| {
|
||||||
|
_ = utils.putCell(@intCast(x1 - 1), @intCast(y1 + i), &c1);
|
||||||
|
_ = utils.putCell(@intCast(x2), @intCast(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(@intCast(x1 + x), @intCast(y1 + y), &blank);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculateComponentCoordinates(self: TerminalBuffer) struct {
|
||||||
|
x: u64,
|
||||||
|
y: u64,
|
||||||
|
visible_length: u64,
|
||||||
|
} {
|
||||||
|
const x = self.box_x + self.margin_box_h + self.labels_max_length + 1;
|
||||||
|
const y = self.box_y + self.margin_box_v;
|
||||||
|
const visible_length = self.box_x + self.box_width - self.margin_box_h - x;
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
.visible_length = visible_length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawLabel(self: TerminalBuffer, text: []const u8, x: u64, y: u64) 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, self.fg, self.bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawConfinedLabel(self: TerminalBuffer, text: []const u8, x: u64, y: u64, max_length: u64) 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: u64, y: u64, length: u64) void {
|
||||||
|
const yc: c_int = @intCast(y);
|
||||||
|
const cell = utils.initCell(char, self.fg, self.bg);
|
||||||
|
|
||||||
|
for (0..length) |xx| _ = utils.putCell(@intCast(x + xx), yc, &cell);
|
||||||
|
}
|
||||||
223
src/tui/components/Desktop.zig
Normal file
223
src/tui/components/Desktop.zig
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const enums = @import("../../enums.zig");
|
||||||
|
const interop = @import("../../interop.zig");
|
||||||
|
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||||
|
const Ini = @import("zigini").Ini;
|
||||||
|
const Lang = @import("../../config/Lang.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const EnvironmentList = std.ArrayList(Environment);
|
||||||
|
|
||||||
|
const DisplayServer = enums.DisplayServer;
|
||||||
|
|
||||||
|
const termbox = interop.termbox;
|
||||||
|
|
||||||
|
const Desktop = @This();
|
||||||
|
|
||||||
|
pub const Environment = struct {
|
||||||
|
entry_ini: ?Ini(Entry) = null,
|
||||||
|
name: [:0]const u8 = "",
|
||||||
|
xdg_session_desktop: [:0]const u8 = "",
|
||||||
|
xdg_desktop_names: ?[:0]const u8 = "",
|
||||||
|
cmd: []const u8 = "",
|
||||||
|
specifier: []const u8 = "",
|
||||||
|
display_server: DisplayServer = .wayland,
|
||||||
|
};
|
||||||
|
|
||||||
|
const DesktopEntry = struct {
|
||||||
|
Exec: []const u8 = "",
|
||||||
|
Name: [:0]const u8 = "",
|
||||||
|
DesktopNames: ?[]const u8 = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Entry = struct { @"Desktop Entry": DesktopEntry = DesktopEntry{} };
|
||||||
|
|
||||||
|
allocator: Allocator,
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
environments: EnvironmentList,
|
||||||
|
current: u64,
|
||||||
|
visible_length: u64,
|
||||||
|
x: u64,
|
||||||
|
y: u64,
|
||||||
|
lang: Lang,
|
||||||
|
|
||||||
|
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: u64, lang: Lang) !Desktop {
|
||||||
|
return .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.buffer = buffer,
|
||||||
|
.environments = try EnvironmentList.initCapacity(allocator, max_length),
|
||||||
|
.current = 0,
|
||||||
|
.visible_length = 0,
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
.lang = lang,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: Desktop) void {
|
||||||
|
for (self.environments.items) |*environment| {
|
||||||
|
if (environment.entry_ini) |*entry_ini| entry_ini.deinit();
|
||||||
|
if (environment.xdg_desktop_names) |desktop_name| self.allocator.free(desktop_name);
|
||||||
|
self.allocator.free(environment.xdg_session_desktop);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.environments.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn position(self: *Desktop, x: u64, y: u64, visible_length: u64) void {
|
||||||
|
self.x = x;
|
||||||
|
self.y = y;
|
||||||
|
self.visible_length = visible_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addEnvironment(self: *Desktop, entry: DesktopEntry, xdg_session_desktop: []const u8, display_server: DisplayServer) !void {
|
||||||
|
var xdg_desktop_names: ?[:0]const u8 = null;
|
||||||
|
if (entry.DesktopNames) |desktop_names| {
|
||||||
|
const desktop_names_z = try self.allocator.dupeZ(u8, desktop_names);
|
||||||
|
for (desktop_names_z) |*c| {
|
||||||
|
if (c.* == ';') c.* = ':';
|
||||||
|
}
|
||||||
|
xdg_desktop_names = desktop_names_z;
|
||||||
|
}
|
||||||
|
|
||||||
|
errdefer {
|
||||||
|
if (xdg_desktop_names) |desktop_names| self.allocator.free(desktop_names);
|
||||||
|
}
|
||||||
|
|
||||||
|
const session_desktop = try self.allocator.dupeZ(u8, xdg_session_desktop);
|
||||||
|
errdefer self.allocator.free(session_desktop);
|
||||||
|
|
||||||
|
try self.environments.append(.{
|
||||||
|
.entry_ini = null,
|
||||||
|
.name = entry.Name,
|
||||||
|
.xdg_session_desktop = session_desktop,
|
||||||
|
.xdg_desktop_names = xdg_desktop_names,
|
||||||
|
.cmd = entry.Exec,
|
||||||
|
.specifier = switch (display_server) {
|
||||||
|
.wayland => self.lang.wayland,
|
||||||
|
.x11 => self.lang.x11,
|
||||||
|
else => self.lang.other,
|
||||||
|
},
|
||||||
|
.display_server = display_server,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.current = self.environments.items.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addEnvironmentWithIni(self: *Desktop, entry_ini: Ini(Entry), xdg_session_desktop: []const u8, display_server: DisplayServer) !void {
|
||||||
|
const entry = entry_ini.data.@"Desktop Entry";
|
||||||
|
var xdg_desktop_names: ?[:0]const u8 = null;
|
||||||
|
if (entry.DesktopNames) |desktop_names| {
|
||||||
|
const desktop_names_z = try self.allocator.dupeZ(u8, desktop_names);
|
||||||
|
for (desktop_names_z) |*c| {
|
||||||
|
if (c.* == ';') c.* = ':';
|
||||||
|
}
|
||||||
|
xdg_desktop_names = desktop_names_z;
|
||||||
|
}
|
||||||
|
|
||||||
|
errdefer {
|
||||||
|
if (xdg_desktop_names) |desktop_names| self.allocator.free(desktop_names);
|
||||||
|
}
|
||||||
|
|
||||||
|
const session_desktop = try self.allocator.dupeZ(u8, xdg_session_desktop);
|
||||||
|
errdefer self.allocator.free(session_desktop);
|
||||||
|
|
||||||
|
try self.environments.append(.{
|
||||||
|
.entry_ini = entry_ini,
|
||||||
|
.name = entry.Name,
|
||||||
|
.xdg_session_desktop = session_desktop,
|
||||||
|
.xdg_desktop_names = xdg_desktop_names,
|
||||||
|
.cmd = entry.Exec,
|
||||||
|
.specifier = switch (display_server) {
|
||||||
|
.wayland => self.lang.wayland,
|
||||||
|
.x11 => self.lang.x11,
|
||||||
|
else => self.lang.other,
|
||||||
|
},
|
||||||
|
.display_server = display_server,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.current = self.environments.items.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn crawl(self: *Desktop, path: []const u8, display_server: DisplayServer) !void {
|
||||||
|
var iterable_directory = std.fs.openDirAbsolute(path, .{ .iterate = true }) catch return;
|
||||||
|
defer iterable_directory.close();
|
||||||
|
|
||||||
|
var iterator = iterable_directory.iterate();
|
||||||
|
while (try iterator.next()) |item| {
|
||||||
|
if (!std.mem.eql(u8, std.fs.path.extension(item.name), ".desktop")) continue;
|
||||||
|
|
||||||
|
const entry_path = try std.fmt.allocPrint(self.allocator, "{s}/{s}", .{ path, item.name });
|
||||||
|
defer self.allocator.free(entry_path);
|
||||||
|
var entry_ini = Ini(Entry).init(self.allocator);
|
||||||
|
_ = try entry_ini.readFileToStruct(entry_path, "#", null);
|
||||||
|
errdefer entry_ini.deinit();
|
||||||
|
|
||||||
|
var xdg_session_desktop: []const u8 = undefined;
|
||||||
|
const maybe_desktop_names = entry_ini.data.@"Desktop Entry".DesktopNames;
|
||||||
|
if (maybe_desktop_names) |desktop_names| {
|
||||||
|
xdg_session_desktop = std.mem.sliceTo(desktop_names, ';');
|
||||||
|
} else {
|
||||||
|
// if DesktopNames is empty, we'll take the name of the session file
|
||||||
|
xdg_session_desktop = std.fs.path.stem(item.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.addEnvironmentWithIni(entry_ini, xdg_session_desktop, display_server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(self: *Desktop, maybe_event: ?*termbox.tb_event, insert_mode: bool) void {
|
||||||
|
if (maybe_event) |event| blk: {
|
||||||
|
if (event.type != termbox.TB_EVENT_KEY) break :blk;
|
||||||
|
|
||||||
|
switch (event.key) {
|
||||||
|
termbox.TB_KEY_ARROW_LEFT, termbox.TB_KEY_CTRL_H => self.goLeft(),
|
||||||
|
termbox.TB_KEY_ARROW_RIGHT, termbox.TB_KEY_CTRL_L => self.goRight(),
|
||||||
|
else => {
|
||||||
|
if (!insert_mode) {
|
||||||
|
switch (event.ch) {
|
||||||
|
'h' => self.goLeft(),
|
||||||
|
'l' => self.goRight(),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = termbox.tb_set_cursor(@intCast(self.x + 2), @intCast(self.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(self: Desktop) void {
|
||||||
|
const environment = self.environments.items[self.current];
|
||||||
|
|
||||||
|
const length = @min(environment.name.len, self.visible_length - 3);
|
||||||
|
if (length == 0) return;
|
||||||
|
|
||||||
|
const x = self.buffer.box_x + self.buffer.margin_box_h;
|
||||||
|
const y = self.buffer.box_y + self.buffer.margin_box_v + 2;
|
||||||
|
self.buffer.drawLabel(environment.specifier, x, y);
|
||||||
|
|
||||||
|
_ = termbox.tb_set_cell(@intCast(self.x), @intCast(self.y), '<', self.buffer.fg, self.buffer.bg);
|
||||||
|
_ = termbox.tb_set_cell(@intCast(self.x + self.visible_length - 1), @intCast(self.y), '>', self.buffer.fg, self.buffer.bg);
|
||||||
|
|
||||||
|
self.buffer.drawLabel(environment.name, self.x + 2, self.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goLeft(self: *Desktop) void {
|
||||||
|
if (self.current == 0) {
|
||||||
|
self.current = self.environments.items.len - 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goRight(self: *Desktop) void {
|
||||||
|
if (self.current == self.environments.items.len - 1) {
|
||||||
|
self.current = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current += 1;
|
||||||
|
}
|
||||||
33
src/tui/components/InfoLine.zig
Normal file
33
src/tui/components/InfoLine.zig
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const utils = @import("../utils.zig");
|
||||||
|
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||||
|
|
||||||
|
const InfoLine = @This();
|
||||||
|
|
||||||
|
text: []const u8 = "",
|
||||||
|
width: u8 = 0,
|
||||||
|
|
||||||
|
pub fn setText(self: *InfoLine, text: []const u8) !void {
|
||||||
|
self.width = if (text.len > 0) try utils.strWidth(text) else 0;
|
||||||
|
self.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(self: InfoLine, buffer: TerminalBuffer) void {
|
||||||
|
if (self.width > 0 and buffer.box_width > self.width) {
|
||||||
|
const label_y = buffer.box_y + buffer.margin_box_v;
|
||||||
|
const x = buffer.box_x + ((buffer.box_width - self.width) / 2);
|
||||||
|
|
||||||
|
buffer.drawLabel(self.text, x, label_y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clearRendered(allocator: std.mem.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);
|
||||||
|
}
|
||||||
149
src/tui/components/Text.zig
Normal file
149
src/tui/components/Text.zig
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
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: u64,
|
||||||
|
cursor: u64,
|
||||||
|
visible_start: u64,
|
||||||
|
visible_length: u64,
|
||||||
|
x: u64,
|
||||||
|
y: u64,
|
||||||
|
|
||||||
|
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: u64) !Text {
|
||||||
|
const text = try DynamicString.initCapacity(allocator, max_length);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.buffer = buffer,
|
||||||
|
.text = text,
|
||||||
|
.end = 0,
|
||||||
|
.cursor = 0,
|
||||||
|
.visible_start = 0,
|
||||||
|
.visible_length = 0,
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: Text) void {
|
||||||
|
self.text.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn position(self: *Text, x: u64, y: u64, visible_length: u64) 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 => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = termbox.tb_set_cursor(@intCast(self.x + (self.cursor - self.visible_start)), @intCast(self.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(self: Text) void {
|
||||||
|
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 drawMasked(self: Text, mask: u8) void {
|
||||||
|
const length = @min(self.text.items.len, self.visible_length - 1);
|
||||||
|
if (length == 0) return;
|
||||||
|
|
||||||
|
self.buffer.drawCharMultiple(mask, self.x, self.y, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(self: *Text) void {
|
||||||
|
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();
|
||||||
|
}
|
||||||
26
src/tui/utils.zig
Normal file
26
src/tui/utils.zig
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const interop = @import("../interop.zig");
|
||||||
|
|
||||||
|
const termbox = interop.termbox;
|
||||||
|
|
||||||
|
pub fn initCell(ch: u32, fg: u16, bg: u16) termbox.tb_cell {
|
||||||
|
return .{
|
||||||
|
.ch = ch,
|
||||||
|
.fg = fg,
|
||||||
|
.bg = bg,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn putCell(x: i32, y: i32, cell: *const termbox.tb_cell) c_int {
|
||||||
|
return termbox.tb_set_cell(x, y, cell.ch, cell.fg, cell.bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Every codepoint is assumed to have a width of 1.
|
||||||
|
// Since ly should be running in a tty, this should be fine.
|
||||||
|
pub fn strWidth(str: []const u8) !u8 {
|
||||||
|
const utf8view = try std.unicode.Utf8View.init(str);
|
||||||
|
var utf8 = utf8view.iterator();
|
||||||
|
var i: u8 = 0;
|
||||||
|
while (utf8.nextCodepoint()) |_| i += 1;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
276
src/utils.c
276
src/utils.c
@@ -1,276 +0,0 @@
|
|||||||
#include "configator.h"
|
|
||||||
#include "dragonfail.h"
|
|
||||||
#include "inputs.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdint.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
15
src/utils.h
@@ -1,15 +0,0 @@
|
|||||||
#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
|
|
||||||
Submodule sub/argoat deleted from e1844c4c94
Submodule sub/configator deleted from 8cec178619
Submodule sub/dragonfail deleted from 15bd3299bf
Submodule sub/termbox_next deleted from d961a81222
Reference in New Issue
Block a user