mirror of https://github.com/fairyglade/ly.git
Compare commits
136 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
32436d438e | |
|
|
ace79f76b5 | |
|
|
35be66e66f | |
|
|
0ac11065f4 | |
|
|
9d8ccf6709 | |
|
|
6f3fdc4708 | |
|
|
0cee1c039a | |
|
|
f03aca0379 | |
|
|
0cd8b2ebfc | |
|
|
4c066ce564 | |
|
|
05c1d4bece | |
|
|
78794b3e10 | |
|
|
afee1d9194 | |
|
|
9b1965a3d8 | |
|
|
741e9e0345 | |
|
|
de8579854c | |
|
|
ee3196bab8 | |
|
|
9ff4ddd129 | |
|
|
b3830d5bb6 | |
|
|
b8ae126623 | |
|
|
4db9295102 | |
|
|
864f5f2892 | |
|
|
c50af66407 | |
|
|
fdf241bed5 | |
|
|
79eebd8ee0 | |
|
|
3869bfd2f9 | |
|
|
5905e054c5 | |
|
|
807f6d249a | |
|
|
15cd0c4779 | |
|
|
51c5c3ee0b | |
|
|
59c07aa3ba | |
|
|
80d4b114f3 | |
|
|
4f45d92ea8 | |
|
|
5edf5251f6 | |
|
|
eec83179b9 | |
|
|
b8048234d9 | |
|
|
10a873acb9 | |
|
|
142476041d | |
|
|
fad683e035 | |
|
|
e882eea22a | |
|
|
fe6942d406 | |
|
|
5b7c7dfdf5 | |
|
|
074bb0a68a | |
|
|
e0a3364169 | |
|
|
7a8d913531 | |
|
|
984ac596af | |
|
|
ed486c29d2 | |
|
|
a6fc5d67e8 | |
|
|
549576aa3e | |
|
|
3758b5da1b | |
|
|
5e1c681385 | |
|
|
ac78ccc398 | |
|
|
e548333473 | |
|
|
60e3380375 | |
|
|
aa392837bc | |
|
|
abe72c74ff | |
|
|
dda56eab37 | |
|
|
9c50297059 | |
|
|
acac884cfe | |
|
|
a89c918c5d | |
|
|
64539f4342 | |
|
|
93696a6b30 | |
|
|
4f26eeada0 | |
|
|
83e98a185f | |
|
|
128dcb16f8 | |
|
|
3a4109eb2d | |
|
|
7cefff4570 | |
|
|
f31c55b562 | |
|
|
9cde291ac7 | |
|
|
03d976171a | |
|
|
b01e4afc79 | |
|
|
5a4605ffb6 | |
|
|
01dcfa207e | |
|
|
32d5330efb | |
|
|
5564fed664 | |
|
|
7c7aed9cb2 | |
|
|
57c96a3478 | |
|
|
6773f74788 | |
|
|
b389e379fa | |
|
|
4a72e41e44 | |
|
|
d268d5bb45 | |
|
|
f320d3f666 | |
|
|
70e95f094a | |
|
|
207b352888 | |
|
|
99dba44e46 | |
|
|
d1810d8c98 | |
|
|
852a602032 | |
|
|
cf5f62661c | |
|
|
1db780c7a7 | |
|
|
941b7e0dae | |
|
|
e9e2d51261 | |
|
|
769aefd6e9 | |
|
|
f678e3bb28 | |
|
|
f22593f828 | |
|
|
7bbdebe58b | |
|
|
bca38856b1 | |
|
|
8c08359e51 | |
|
|
fd81da7cbd | |
|
|
950eeed3ee | |
|
|
ce0d00771d | |
|
|
b032c9b296 | |
|
|
7744745f09 | |
|
|
11735290b8 | |
|
|
21fca058e7 | |
|
|
2b46a81796 | |
|
|
72f43fbc56 | |
|
|
b00d6899e5 | |
|
|
7ce8ff61fe | |
|
|
1a04a608a1 | |
|
|
b0dcc12785 | |
|
|
b672d04dc6 | |
|
|
2e04ea4d79 | |
|
|
0c12008327 | |
|
|
7934060d3b | |
|
|
a158098df0 | |
|
|
5bfa1670cc | |
|
|
a4076b83da | |
|
|
2eea683078 | |
|
|
d7f64676ee | |
|
|
456916f059 | |
|
|
2b1e4dc6c9 | |
|
|
94c306758a | |
|
|
82d24d7725 | |
|
|
5a51d5ced5 | |
|
|
135d1e40f6 | |
|
|
c0c400e0b6 | |
|
|
9e4147bfb4 | |
|
|
8e893932f2 | |
|
|
26e7585b0b | |
|
|
b1cb576f67 | |
|
|
add7f25f0d | |
|
|
2e7bb3eb58 | |
|
|
e57de5172e | |
|
|
04d4447273 | |
|
|
ced8f9bee3 | |
|
|
c6446db3e2 |
|
|
@ -0,0 +1,2 @@
|
||||||
|
github: AnErrupTion
|
||||||
|
liberapay: ShiningLea
|
||||||
|
|
@ -12,8 +12,8 @@ body:
|
||||||
- label: I have looked for any other duplicate issues
|
- label: I have looked for any other duplicate issues
|
||||||
required: true
|
required: true
|
||||||
- label: I have reproduced the issue on a fresh install of my OS & Ly with default settings, except ones I will mention
|
- label: I have reproduced the issue on a fresh install of my OS & Ly with default settings, except ones I will mention
|
||||||
required: true
|
required: false
|
||||||
- label: I have confirmed this issue also occurs on the latest development version
|
- label: I have confirmed this issue also occurs on the latest development version (found in the `master` branch)
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: version
|
id: version
|
||||||
|
|
|
||||||
|
|
@ -9,3 +9,4 @@ _Replace this with a reference to an existing issue, or N/A if there is none_
|
||||||
## Pre-requisites
|
## Pre-requisites
|
||||||
|
|
||||||
- [ ] I have tested & confirmed the changes work locally
|
- [ ] I have tested & confirmed the changes work locally
|
||||||
|
- [ ] I have run `zig fmt` throughout my changes
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 52 KiB |
|
|
@ -3,3 +3,4 @@ zig-cache/
|
||||||
zig-out/
|
zig-out/
|
||||||
valgrind.log
|
valgrind.log
|
||||||
.zig-cache
|
.zig-cache
|
||||||
|
zig-pkg
|
||||||
|
|
|
||||||
298
build.zig
298
build.zig
|
|
@ -12,7 +12,7 @@ const InitSystem = enum {
|
||||||
freebsd,
|
freebsd,
|
||||||
};
|
};
|
||||||
|
|
||||||
const min_zig_string = "0.15.0";
|
const min_zig_string = "0.16.0";
|
||||||
const current_zig = builtin.zig_version;
|
const current_zig = builtin.zig_version;
|
||||||
|
|
||||||
// Implementing zig version detection through compile time
|
// Implementing zig version detection through compile time
|
||||||
|
|
@ -23,7 +23,7 @@ comptime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ly_version = std.SemanticVersion{ .major = 1, .minor = 3, .patch = 0 };
|
const ly_version = std.SemanticVersion{ .major = 1, .minor = 5, .patch = 0 };
|
||||||
|
|
||||||
var dest_directory: []const u8 = undefined;
|
var dest_directory: []const u8 = undefined;
|
||||||
var config_directory: []const u8 = undefined;
|
var config_directory: []const u8 = undefined;
|
||||||
|
|
@ -67,37 +67,24 @@ pub fn build(b: *std.Build) !void {
|
||||||
.root_source_file = b.path("src/main.zig"),
|
.root_source_file = b.path("src/main.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
|
.link_libc = true,
|
||||||
}),
|
}),
|
||||||
// Here until the native backend matures in terms of performance
|
|
||||||
.use_llvm = true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const zigini = b.dependency("zigini", .{ .target = target, .optimize = optimize });
|
const ly_ui = b.dependency("ly_ui", .{
|
||||||
exe.root_module.addImport("zigini", zigini.module("zigini"));
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.enable_x11_support = enable_x11_support,
|
||||||
|
});
|
||||||
|
exe.root_module.addImport("ly-ui", ly_ui.module("ly-ui"));
|
||||||
|
|
||||||
exe.root_module.addOptions("build_options", build_options);
|
exe.root_module.addOptions("build_options", build_options);
|
||||||
|
|
||||||
const clap = b.dependency("clap", .{ .target = target, .optimize = optimize });
|
const clap = b.dependency("clap", .{ .target = target, .optimize = optimize });
|
||||||
exe.root_module.addImport("clap", clap.module("clap"));
|
exe.root_module.addImport("clap", clap.module("clap"));
|
||||||
|
|
||||||
const termbox_dep = b.dependency("termbox2", .{
|
exe.root_module.linkSystemLibrary("pam", .{});
|
||||||
.target = target,
|
if (enable_x11_support) exe.root_module.linkSystemLibrary("xcb", .{});
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
|
|
||||||
exe.linkSystemLibrary("pam");
|
|
||||||
if (enable_x11_support) exe.linkSystemLibrary("xcb");
|
|
||||||
exe.linkLibC();
|
|
||||||
|
|
||||||
const translate_c = b.addTranslateC(.{
|
|
||||||
.root_source_file = termbox_dep.path("termbox2.h"),
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
translate_c.defineCMacroRaw("TB_IMPL");
|
|
||||||
translate_c.defineCMacro("TB_OPT_ATTR_W", "32"); // Enable 24-bit color support + styling (32-bit)
|
|
||||||
const termbox2 = translate_c.addModule("termbox2");
|
|
||||||
exe.root_module.addImport("termbox2", termbox2);
|
|
||||||
|
|
||||||
b.installArtifact(exe);
|
b.installArtifact(exe);
|
||||||
|
|
||||||
|
|
@ -128,6 +115,8 @@ pub fn build(b: *std.Build) !void {
|
||||||
pub fn Installer(install_config: bool) type {
|
pub fn Installer(install_config: bool) type {
|
||||||
return struct {
|
return struct {
|
||||||
pub fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
|
pub fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
|
||||||
|
var threaded: std.Io.Threaded = .init_single_threaded;
|
||||||
|
const io = threaded.io();
|
||||||
const allocator = step.owner.allocator;
|
const allocator = step.owner.allocator;
|
||||||
|
|
||||||
var patch_map = PatchMap.init(allocator);
|
var patch_map = PatchMap.init(allocator);
|
||||||
|
|
@ -142,71 +131,75 @@ pub fn Installer(install_config: bool) type {
|
||||||
// instead to shutdown the system.
|
// instead to shutdown the system.
|
||||||
try patch_map.put("$PLATFORM_SHUTDOWN_ARG", if (init_system == .freebsd) "-p" else "-a");
|
try patch_map.put("$PLATFORM_SHUTDOWN_ARG", if (init_system == .freebsd) "-p" else "-a");
|
||||||
|
|
||||||
try install_ly(allocator, patch_map, install_config);
|
try install_ly(allocator, io, patch_map, install_config);
|
||||||
try install_service(allocator, patch_map);
|
try install_service(allocator, io, patch_map);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn install_ly(allocator: std.mem.Allocator, patch_map: PatchMap, install_config: bool) !void {
|
fn install_ly(allocator: std.mem.Allocator, io: std.Io, patch_map: PatchMap, install_config: bool) !void {
|
||||||
const ly_config_directory = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly" });
|
const ly_config_directory = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly" });
|
||||||
|
|
||||||
std.fs.cwd().makePath(ly_config_directory) catch {
|
std.Io.Dir.cwd().createDirPath(io, ly_config_directory) catch {
|
||||||
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_config_directory});
|
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_config_directory});
|
||||||
};
|
};
|
||||||
|
|
||||||
const ly_custom_sessions_directory = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/custom-sessions" });
|
const ly_custom_sessions_directory = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/custom-sessions" });
|
||||||
|
|
||||||
std.fs.cwd().makePath(ly_custom_sessions_directory) catch {
|
std.Io.Dir.cwd().createDirPath(io, ly_custom_sessions_directory) catch {
|
||||||
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_custom_sessions_directory});
|
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_custom_sessions_directory});
|
||||||
};
|
};
|
||||||
|
|
||||||
const ly_lang_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/lang" });
|
const ly_lang_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/lang" });
|
||||||
std.fs.cwd().makePath(ly_lang_path) catch {
|
std.Io.Dir.cwd().createDirPath(io, ly_lang_path) catch {
|
||||||
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_lang_path});
|
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_lang_path});
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin" });
|
const exe_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin" });
|
||||||
std.fs.cwd().makePath(exe_path) catch {
|
std.Io.Dir.cwd().createDirPath(io, exe_path) catch {
|
||||||
if (!std.mem.eql(u8, dest_directory, "")) {
|
if (!std.mem.eql(u8, dest_directory, "")) {
|
||||||
std.debug.print("warn: {s} already exists as a directory.\n", .{exe_path});
|
std.debug.print("warn: {s} already exists as a directory.\n", .{exe_path});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var executable_dir = std.fs.cwd().openDir(exe_path, .{}) catch unreachable;
|
var executable_dir = std.Io.Dir.cwd().openDir(io, exe_path, .{}) catch unreachable;
|
||||||
defer executable_dir.close();
|
defer executable_dir.close(io);
|
||||||
|
|
||||||
try installFile("zig-out/bin/ly", executable_dir, exe_path, executable_name, .{});
|
try installFile(io, "zig-out/bin/ly", executable_dir, exe_path, executable_name, .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var config_dir = std.fs.cwd().openDir(ly_config_directory, .{}) catch unreachable;
|
var config_dir = std.Io.Dir.cwd().openDir(io, ly_config_directory, .{}) catch unreachable;
|
||||||
defer config_dir.close();
|
defer config_dir.close(io);
|
||||||
|
|
||||||
if (install_config) {
|
if (install_config) {
|
||||||
const patched_config = try patchFile(allocator, "res/config.ini", patch_map);
|
const patched_config = try patchFile(allocator, io, "res/config.ini", patch_map);
|
||||||
try installText(patched_config, config_dir, ly_config_directory, "config.ini", .{});
|
try installText(io, patched_config, config_dir, ly_config_directory, "config.ini", .{});
|
||||||
|
|
||||||
|
try installFile(io, "res/startup.sh", config_dir, ly_config_directory, "startup.sh", .{ .permissions = .fromMode(0o755) });
|
||||||
}
|
}
|
||||||
|
|
||||||
const patched_example_config = try patchFile(allocator, "res/config.ini", patch_map);
|
const patched_example_config = try patchFile(allocator, io, "res/config.ini", patch_map);
|
||||||
try installText(patched_example_config, config_dir, ly_config_directory, "config.ini.example", .{});
|
try installText(io, patched_example_config, config_dir, ly_config_directory, "config.ini.example", .{});
|
||||||
|
|
||||||
const patched_setup = try patchFile(allocator, "res/setup.sh", patch_map);
|
const patched_setup = try patchFile(allocator, io, "res/setup.sh", patch_map);
|
||||||
try installText(patched_setup, config_dir, ly_config_directory, "setup.sh", .{ .mode = 0o755 });
|
try installText(io, patched_setup, config_dir, ly_config_directory, "setup.sh", .{ .permissions = .fromMode(0o755) });
|
||||||
|
|
||||||
|
try installFile(io, "res/example.dur", config_dir, ly_config_directory, "example.dur", .{ .permissions = .fromMode(0o755) });
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var custom_sessions_dir = std.fs.cwd().openDir(ly_custom_sessions_directory, .{}) catch unreachable;
|
var custom_sessions_dir = std.Io.Dir.cwd().openDir(io, ly_custom_sessions_directory, .{}) catch unreachable;
|
||||||
defer custom_sessions_dir.close();
|
defer custom_sessions_dir.close(io);
|
||||||
|
|
||||||
const patched_readme = try patchFile(allocator, "res/custom-sessions/README", patch_map);
|
const patched_readme = try patchFile(allocator, io, "res/custom-sessions/README", patch_map);
|
||||||
try installText(patched_readme, custom_sessions_dir, ly_custom_sessions_directory, "README", .{});
|
try installText(io, patched_readme, custom_sessions_dir, ly_custom_sessions_directory, "README", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var lang_dir = std.fs.cwd().openDir(ly_lang_path, .{}) catch unreachable;
|
var lang_dir = std.Io.Dir.cwd().openDir(io, ly_lang_path, .{}) catch unreachable;
|
||||||
defer lang_dir.close();
|
defer lang_dir.close(io);
|
||||||
|
|
||||||
const languages = [_][]const u8{
|
const languages = [_][]const u8{
|
||||||
"ar.ini",
|
"ar.ini",
|
||||||
|
|
@ -232,63 +225,66 @@ fn install_ly(allocator: std.mem.Allocator, patch_map: PatchMap, install_config:
|
||||||
};
|
};
|
||||||
|
|
||||||
inline for (languages) |language| {
|
inline for (languages) |language| {
|
||||||
try installFile("res/lang/" ++ language, lang_dir, ly_lang_path, language, .{});
|
try installFile(io, "res/lang/" ++ language, lang_dir, ly_lang_path, language, .{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const pam_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/pam.d" });
|
const pam_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/pam.d" });
|
||||||
std.fs.cwd().makePath(pam_path) catch {
|
std.Io.Dir.cwd().createDirPath(io, pam_path) catch {
|
||||||
if (!std.mem.eql(u8, dest_directory, "")) {
|
if (!std.mem.eql(u8, dest_directory, "")) {
|
||||||
std.debug.print("warn: {s} already exists as a directory.\n", .{pam_path});
|
std.debug.print("warn: {s} already exists as a directory.\n", .{pam_path});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var pam_dir = std.fs.cwd().openDir(pam_path, .{}) catch unreachable;
|
var pam_dir = std.Io.Dir.cwd().openDir(io, pam_path, .{}) catch unreachable;
|
||||||
defer pam_dir.close();
|
defer pam_dir.close(io);
|
||||||
|
|
||||||
try installFile(if (init_system == .freebsd) "res/pam.d/ly-freebsd" else "res/pam.d/ly-linux", pam_dir, pam_path, "ly", .{ .override_mode = 0o644 });
|
try installFile(io, if (init_system == .freebsd) "res/pam.d/ly-freebsd" else "res/pam.d/ly-linux", pam_dir, pam_path, "ly", .{ .permissions = .fromMode(0o644) });
|
||||||
try installFile(if (init_system == .freebsd) "res/pam.d/ly-freebsd-autologin" else "res/pam.d/ly-linux-autologin", pam_dir, pam_path, "ly-autologin", .{ .override_mode = 0o644 });
|
try installFile(io, if (init_system == .freebsd) "res/pam.d/ly-freebsd-autologin" else "res/pam.d/ly-linux-autologin", pam_dir, pam_path, "ly-autologin", .{ .permissions = .fromMode(0o644) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn install_service(allocator: std.mem.Allocator, patch_map: PatchMap) !void {
|
fn install_service(allocator: std.mem.Allocator, io: std.Io, patch_map: PatchMap) !void {
|
||||||
switch (init_system) {
|
switch (init_system) {
|
||||||
.systemd => {
|
.systemd => {
|
||||||
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/lib/systemd/system" });
|
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/lib/systemd/system" });
|
||||||
std.fs.cwd().makePath(service_path) catch {};
|
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
|
||||||
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
|
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
|
||||||
defer service_dir.close();
|
defer service_dir.close(io);
|
||||||
|
|
||||||
const patched_service = try patchFile(allocator, "res/ly@.service", patch_map);
|
const patched_service = try patchFile(allocator, io, "res/ly@.service", patch_map);
|
||||||
try installText(patched_service, service_dir, service_path, "ly@.service", .{ .mode = 0o644 });
|
try installText(io, patched_service, service_dir, service_path, "ly@.service", .{ .permissions = .fromMode(0o644) });
|
||||||
|
|
||||||
|
const patched_kmsconvt_service = try patchFile(allocator, io, "res/ly-kmsconvt@.service", patch_map);
|
||||||
|
try installText(io, patched_kmsconvt_service, service_dir, service_path, "ly-kmsconvt@.service", .{ .permissions = .fromMode(0o644) });
|
||||||
},
|
},
|
||||||
.openrc => {
|
.openrc => {
|
||||||
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
|
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
|
||||||
std.fs.cwd().makePath(service_path) catch {};
|
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
|
||||||
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
|
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
|
||||||
defer service_dir.close();
|
defer service_dir.close(io);
|
||||||
|
|
||||||
const patched_service = try patchFile(allocator, "res/ly-openrc", patch_map);
|
const patched_service = try patchFile(allocator, io, "res/ly-openrc", patch_map);
|
||||||
try installText(patched_service, service_dir, service_path, executable_name, .{ .mode = 0o755 });
|
try installText(io, patched_service, service_dir, service_path, executable_name, .{ .permissions = .fromMode(0o755) });
|
||||||
},
|
},
|
||||||
.runit => {
|
.runit => {
|
||||||
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/sv/ly" });
|
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/sv/ly" });
|
||||||
std.fs.cwd().makePath(service_path) catch {};
|
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
|
||||||
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
|
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
|
||||||
defer service_dir.close();
|
defer service_dir.close(io);
|
||||||
|
|
||||||
const supervise_path = try std.fs.path.join(allocator, &[_][]const u8{ service_path, "supervise" });
|
const supervise_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ service_path, "supervise" });
|
||||||
|
|
||||||
const patched_conf = try patchFile(allocator, "res/ly-runit-service/conf", patch_map);
|
const patched_conf = try patchFile(allocator, io, "res/ly-runit-service/conf", patch_map);
|
||||||
try installText(patched_conf, service_dir, service_path, "conf", .{});
|
try installText(io, patched_conf, service_dir, service_path, "conf", .{});
|
||||||
|
|
||||||
try installFile("res/ly-runit-service/finish", service_dir, service_path, "finish", .{ .override_mode = 0o755 });
|
try installFile(io, "res/ly-runit-service/finish", service_dir, service_path, "finish", .{ .permissions = .fromMode(0o755) });
|
||||||
|
|
||||||
const patched_run = try patchFile(allocator, "res/ly-runit-service/run", patch_map);
|
const patched_run = try patchFile(allocator, io, "res/ly-runit-service/run", patch_map);
|
||||||
try installText(patched_run, service_dir, service_path, "run", .{ .mode = 0o755 });
|
try installText(io, patched_run, service_dir, service_path, "run", .{ .permissions = .fromMode(0o755) });
|
||||||
|
|
||||||
std.fs.cwd().symLink("/run/runit/supervise.ly", supervise_path, .{}) catch |err| {
|
std.Io.Dir.cwd().symLink(io, "/run/runit/supervise.ly", supervise_path, .{}) catch |err| {
|
||||||
if (err == error.PathAlreadyExists) {
|
if (err == error.PathAlreadyExists) {
|
||||||
std.debug.print("warn: /run/runit/supervise.ly already exists as a symbolic link.\n", .{});
|
std.debug.print("warn: /run/runit/supervise.ly already exists as a symbolic link.\n", .{});
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -298,49 +294,49 @@ fn install_service(allocator: std.mem.Allocator, patch_map: PatchMap) !void {
|
||||||
std.debug.print("info: installed symlink /run/runit/supervise.ly\n", .{});
|
std.debug.print("info: installed symlink /run/runit/supervise.ly\n", .{});
|
||||||
},
|
},
|
||||||
.s6 => {
|
.s6 => {
|
||||||
const admin_service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/adminsv/default/contents.d" });
|
const admin_service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/adminsv/default/contents.d" });
|
||||||
std.fs.cwd().makePath(admin_service_path) catch {};
|
std.Io.Dir.cwd().createDirPath(io, admin_service_path) catch {};
|
||||||
var admin_service_dir = std.fs.cwd().openDir(admin_service_path, .{}) catch unreachable;
|
var admin_service_dir = std.Io.Dir.cwd().openDir(io, admin_service_path, .{}) catch unreachable;
|
||||||
defer admin_service_dir.close();
|
defer admin_service_dir.close(io);
|
||||||
|
|
||||||
const file = try admin_service_dir.createFile("ly-srv", .{});
|
const file = try admin_service_dir.createFile(io, "ly-srv", .{});
|
||||||
file.close();
|
file.close(io);
|
||||||
|
|
||||||
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/sv/ly-srv" });
|
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/sv/ly-srv" });
|
||||||
std.fs.cwd().makePath(service_path) catch {};
|
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
|
||||||
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
|
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
|
||||||
defer service_dir.close();
|
defer service_dir.close(io);
|
||||||
|
|
||||||
const patched_run = try patchFile(allocator, "res/ly-s6/run", patch_map);
|
const patched_run = try patchFile(allocator, io, "res/ly-s6/run", patch_map);
|
||||||
try installText(patched_run, service_dir, service_path, "run", .{ .mode = 0o755 });
|
try installText(io, patched_run, service_dir, service_path, "run", .{ .permissions = .fromMode(0o755) });
|
||||||
|
|
||||||
try installFile("res/ly-s6/type", service_dir, service_path, "type", .{});
|
try installFile(io, "res/ly-s6/type", service_dir, service_path, "type", .{});
|
||||||
},
|
},
|
||||||
.dinit => {
|
.dinit => {
|
||||||
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/dinit.d" });
|
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/dinit.d" });
|
||||||
std.fs.cwd().makePath(service_path) catch {};
|
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
|
||||||
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
|
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
|
||||||
defer service_dir.close();
|
defer service_dir.close(io);
|
||||||
|
|
||||||
const patched_service = try patchFile(allocator, "res/ly-dinit", patch_map);
|
const patched_service = try patchFile(allocator, io, "res/ly-dinit", patch_map);
|
||||||
try installText(patched_service, service_dir, service_path, "ly", .{});
|
try installText(io, patched_service, service_dir, service_path, "ly", .{});
|
||||||
},
|
},
|
||||||
.sysvinit => {
|
.sysvinit => {
|
||||||
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
|
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
|
||||||
std.fs.cwd().makePath(service_path) catch {};
|
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
|
||||||
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
|
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
|
||||||
defer service_dir.close();
|
defer service_dir.close(io);
|
||||||
|
|
||||||
const patched_service = try patchFile(allocator, "res/ly-sysvinit", patch_map);
|
const patched_service = try patchFile(allocator, io, "res/ly-sysvinit", patch_map);
|
||||||
try installText(patched_service, service_dir, service_path, "ly", .{ .mode = 0o755 });
|
try installText(io, patched_service, service_dir, service_path, "ly", .{ .permissions = .fromMode(0o755) });
|
||||||
},
|
},
|
||||||
.freebsd => {
|
.freebsd => {
|
||||||
const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin" });
|
const exe_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin" });
|
||||||
var executable_dir = std.fs.cwd().openDir(exe_path, .{}) catch unreachable;
|
var executable_dir = std.Io.Dir.cwd().openDir(io, exe_path, .{}) catch unreachable;
|
||||||
defer executable_dir.close();
|
defer executable_dir.close(io);
|
||||||
|
|
||||||
const patched_wrapper = try patchFile(allocator, "res/ly-freebsd-wrapper", patch_map);
|
const patched_wrapper = try patchFile(allocator, io, "res/ly-freebsd-wrapper", patch_map);
|
||||||
try installText(patched_wrapper, executable_dir, exe_path, "ly_wrapper", .{ .mode = 0o755 });
|
try installText(io, patched_wrapper, executable_dir, exe_path, "ly_wrapper", .{ .permissions = .fromMode(0o755) });
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -348,33 +344,35 @@ fn install_service(allocator: std.mem.Allocator, patch_map: PatchMap) !void {
|
||||||
pub fn Uninstaller(uninstall_config: bool) type {
|
pub fn Uninstaller(uninstall_config: bool) type {
|
||||||
return struct {
|
return struct {
|
||||||
pub fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
|
pub fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
|
||||||
|
var threaded: std.Io.Threaded = .init_single_threaded;
|
||||||
|
const io = threaded.io();
|
||||||
const allocator = step.owner.allocator;
|
const allocator = step.owner.allocator;
|
||||||
|
|
||||||
if (uninstall_config) {
|
if (uninstall_config) {
|
||||||
try deleteTree(allocator, config_directory, "/ly", "ly config directory not found");
|
try deleteTree(allocator, io, config_directory, "/ly", "ly config directory not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ prefix_directory, "/bin/", executable_name });
|
const exe_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ prefix_directory, "/bin/", executable_name });
|
||||||
var success = true;
|
var success = true;
|
||||||
std.fs.cwd().deleteFile(exe_path) catch {
|
std.Io.Dir.cwd().deleteFile(io, exe_path) catch {
|
||||||
std.debug.print("warn: ly executable not found\n", .{});
|
std.debug.print("warn: ly executable not found\n", .{});
|
||||||
success = false;
|
success = false;
|
||||||
};
|
};
|
||||||
if (success) std.debug.print("info: deleted {s}\n", .{exe_path});
|
if (success) std.debug.print("info: deleted {s}\n", .{exe_path});
|
||||||
|
|
||||||
try deleteFile(allocator, config_directory, "/pam.d/ly", "ly pam file not found");
|
try deleteFile(allocator, io, config_directory, "/pam.d/ly", "ly pam file not found");
|
||||||
|
|
||||||
switch (init_system) {
|
switch (init_system) {
|
||||||
.systemd => try deleteFile(allocator, prefix_directory, "/lib/systemd/system/ly@.service", "systemd service not found"),
|
.systemd => try deleteFile(allocator, io, prefix_directory, "/lib/systemd/system/ly@.service", "systemd service not found"),
|
||||||
.openrc => try deleteFile(allocator, config_directory, "/init.d/ly", "openrc service not found"),
|
.openrc => try deleteFile(allocator, io, config_directory, "/init.d/ly", "openrc service not found"),
|
||||||
.runit => try deleteTree(allocator, config_directory, "/sv/ly", "runit service not found"),
|
.runit => try deleteTree(allocator, io, config_directory, "/sv/ly", "runit service not found"),
|
||||||
.s6 => {
|
.s6 => {
|
||||||
try deleteTree(allocator, config_directory, "/s6/sv/ly-srv", "s6 service not found");
|
try deleteTree(allocator, io, config_directory, "/s6/sv/ly-srv", "s6 service not found");
|
||||||
try deleteFile(allocator, config_directory, "/s6/adminsv/default/contents.d/ly-srv", "s6 admin service not found");
|
try deleteFile(allocator, io, config_directory, "/s6/adminsv/default/contents.d/ly-srv", "s6 admin service not found");
|
||||||
},
|
},
|
||||||
.dinit => try deleteFile(allocator, config_directory, "/dinit.d/ly", "dinit service not found"),
|
.dinit => try deleteFile(allocator, io, config_directory, "/dinit.d/ly", "dinit service not found"),
|
||||||
.sysvinit => try deleteFile(allocator, config_directory, "/init.d/ly", "sysvinit service not found"),
|
.sysvinit => try deleteFile(allocator, io, config_directory, "/init.d/ly", "sysvinit service not found"),
|
||||||
.freebsd => try deleteFile(allocator, prefix_directory, "/bin/ly_wrapper", "freebsd wrapper not found"),
|
.freebsd => try deleteFile(allocator, io, prefix_directory, "/bin/ly_wrapper", "freebsd wrapper not found"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -392,11 +390,11 @@ fn getVersionStr(b: *std.Build, name: []const u8, version: std.SemanticVersion)
|
||||||
"--match",
|
"--match",
|
||||||
"*.*.*",
|
"*.*.*",
|
||||||
"--tags",
|
"--tags",
|
||||||
}, &status, .Ignore) catch {
|
}, &status, .ignore) catch {
|
||||||
return version_str;
|
return version_str;
|
||||||
};
|
};
|
||||||
var git_describe = std.mem.trim(u8, git_describe_raw, " \n\r");
|
var git_describe = std.mem.trim(u8, git_describe_raw, " \n\r");
|
||||||
git_describe = std.mem.trimLeft(u8, git_describe, "v");
|
git_describe = std.mem.trimStart(u8, git_describe, "v");
|
||||||
|
|
||||||
switch (std.mem.count(u8, git_describe, "-")) {
|
switch (std.mem.count(u8, git_describe, "-")) {
|
||||||
0 => {
|
0 => {
|
||||||
|
|
@ -409,7 +407,7 @@ fn getVersionStr(b: *std.Build, name: []const u8, version: std.SemanticVersion)
|
||||||
2 => {
|
2 => {
|
||||||
// Untagged development build (e.g. 0.10.0-dev.2025+ecf0050a9).
|
// Untagged development build (e.g. 0.10.0-dev.2025+ecf0050a9).
|
||||||
var it = std.mem.splitScalar(u8, git_describe, '-');
|
var it = std.mem.splitScalar(u8, git_describe, '-');
|
||||||
const tagged_ancestor = std.mem.trimLeft(u8, it.first(), "v");
|
const tagged_ancestor = std.mem.trimStart(u8, it.first(), "v");
|
||||||
const commit_height = it.next().?;
|
const commit_height = it.next().?;
|
||||||
const commit_id = it.next().?;
|
const commit_id = it.next().?;
|
||||||
|
|
||||||
|
|
@ -436,25 +434,26 @@ fn getVersionStr(b: *std.Build, name: []const u8, version: std.SemanticVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn installFile(
|
fn installFile(
|
||||||
|
io: std.Io,
|
||||||
source_file: []const u8,
|
source_file: []const u8,
|
||||||
destination_directory: std.fs.Dir,
|
destination_directory: std.Io.Dir,
|
||||||
destination_directory_path: []const u8,
|
destination_directory_path: []const u8,
|
||||||
destination_file: []const u8,
|
destination_file: []const u8,
|
||||||
options: std.fs.Dir.CopyFileOptions,
|
options: std.Io.Dir.CopyFileOptions,
|
||||||
) !void {
|
) !void {
|
||||||
try std.fs.cwd().copyFile(source_file, destination_directory, destination_file, options);
|
try std.Io.Dir.cwd().copyFile(source_file, destination_directory, destination_file, io, options);
|
||||||
std.debug.print("info: installed {s}/{s}\n", .{ destination_directory_path, destination_file });
|
std.debug.print("info: installed {s}/{s}\n", .{ destination_directory_path, destination_file });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn patchFile(allocator: std.mem.Allocator, source_file: []const u8, patch_map: PatchMap) ![]const u8 {
|
fn patchFile(allocator: std.mem.Allocator, io: std.Io, source_file: []const u8, patch_map: PatchMap) ![]const u8 {
|
||||||
var file = try std.fs.cwd().openFile(source_file, .{});
|
var file = try std.Io.Dir.cwd().openFile(io, source_file, .{});
|
||||||
defer file.close();
|
defer file.close(io);
|
||||||
|
|
||||||
const stat = try file.stat();
|
const stat = try file.stat(io);
|
||||||
|
|
||||||
var buffer: [4096]u8 = undefined;
|
var buffer: [4096]u8 = undefined;
|
||||||
var reader = file.reader(&buffer);
|
var reader = file.reader(io, &buffer);
|
||||||
var text = try reader.interface.readAlloc(allocator, stat.size);
|
var text = try reader.interface.readAlloc(allocator, @intCast(stat.size));
|
||||||
|
|
||||||
var iterator = patch_map.iterator();
|
var iterator = patch_map.iterator();
|
||||||
while (iterator.next()) |kv| {
|
while (iterator.next()) |kv| {
|
||||||
|
|
@ -467,17 +466,18 @@ fn patchFile(allocator: std.mem.Allocator, source_file: []const u8, patch_map: P
|
||||||
}
|
}
|
||||||
|
|
||||||
fn installText(
|
fn installText(
|
||||||
|
io: std.Io,
|
||||||
text: []const u8,
|
text: []const u8,
|
||||||
destination_directory: std.fs.Dir,
|
destination_directory: std.Io.Dir,
|
||||||
destination_directory_path: []const u8,
|
destination_directory_path: []const u8,
|
||||||
destination_file: []const u8,
|
destination_file: []const u8,
|
||||||
options: std.fs.File.CreateFlags,
|
options: std.Io.File.CreateFlags,
|
||||||
) !void {
|
) !void {
|
||||||
var file = try destination_directory.createFile(destination_file, options);
|
var file = try destination_directory.createFile(io, destination_file, options);
|
||||||
defer file.close();
|
defer file.close(io);
|
||||||
|
|
||||||
var buffer: [1024]u8 = undefined;
|
var buffer: [1024]u8 = undefined;
|
||||||
var writer = file.writer(&buffer);
|
var writer = file.writer(io, &buffer);
|
||||||
try writer.interface.writeAll(text);
|
try writer.interface.writeAll(text);
|
||||||
try writer.interface.flush();
|
try writer.interface.flush();
|
||||||
|
|
||||||
|
|
@ -486,13 +486,14 @@ fn installText(
|
||||||
|
|
||||||
fn deleteFile(
|
fn deleteFile(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
io: std.Io,
|
||||||
prefix: []const u8,
|
prefix: []const u8,
|
||||||
file: []const u8,
|
file: []const u8,
|
||||||
warning: []const u8,
|
warning: []const u8,
|
||||||
) !void {
|
) !void {
|
||||||
const path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix, file });
|
const path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix, file });
|
||||||
|
|
||||||
std.fs.cwd().deleteFile(path) catch |err| {
|
std.Io.Dir.cwd().deleteFile(io, path) catch |err| {
|
||||||
if (err == error.FileNotFound) {
|
if (err == error.FileNotFound) {
|
||||||
std.debug.print("warn: {s}\n", .{warning});
|
std.debug.print("warn: {s}\n", .{warning});
|
||||||
return;
|
return;
|
||||||
|
|
@ -506,13 +507,14 @@ fn deleteFile(
|
||||||
|
|
||||||
fn deleteTree(
|
fn deleteTree(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
io: std.Io,
|
||||||
prefix: []const u8,
|
prefix: []const u8,
|
||||||
directory: []const u8,
|
directory: []const u8,
|
||||||
warning: []const u8,
|
warning: []const u8,
|
||||||
) !void {
|
) !void {
|
||||||
const path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix, directory });
|
const path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix, directory });
|
||||||
|
|
||||||
var dir = std.fs.cwd().openDir(path, .{}) catch |err| {
|
var dir = std.Io.Dir.cwd().openDir(io, path, .{}) catch |err| {
|
||||||
if (err == error.FileNotFound) {
|
if (err == error.FileNotFound) {
|
||||||
std.debug.print("warn: {s}\n", .{warning});
|
std.debug.print("warn: {s}\n", .{warning});
|
||||||
return;
|
return;
|
||||||
|
|
@ -520,9 +522,9 @@ fn deleteTree(
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
dir.close();
|
dir.close(io);
|
||||||
|
|
||||||
try std.fs.cwd().deleteTree(path);
|
try std.Io.Dir.cwd().deleteTree(io, path);
|
||||||
|
|
||||||
std.debug.print("info: deleted {s}\n", .{path});
|
std.debug.print("info: deleted {s}\n", .{path});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,20 @@
|
||||||
.{
|
.{
|
||||||
.name = .ly,
|
.name = .ly,
|
||||||
.version = "1.3.0",
|
.version = "1.5.0",
|
||||||
.fingerprint = 0xa148ffcc5dc2cb59,
|
.fingerprint = 0xa148ffcc5dc2cb59,
|
||||||
.minimum_zig_version = "0.15.0",
|
.minimum_zig_version = "0.16.0",
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
|
.ly_ui = .{
|
||||||
|
.path = "ly-ui",
|
||||||
|
},
|
||||||
.clap = .{
|
.clap = .{
|
||||||
.url = "git+https://github.com/Hejsil/zig-clap#5289e0753cd274d65344bef1c114284c633536ea",
|
.url = "git+https://github.com/Hejsil/zig-clap#fc1e5cc3f6d9d3001112385ee6256d694e959d2f",
|
||||||
.hash = "clap-0.11.0-oBajB-HnAQDPCKYzwF7rO3qDFwRcD39Q0DALlTSz5H7e",
|
.hash = "clap-0.11.0-oBajB7foAQC3Iyn4IVCkUdYaOVVng5IZkSncySTjNig1",
|
||||||
},
|
|
||||||
.zigini = .{
|
|
||||||
.url = "git+https://github.com/AnErrupTion/zigini?ref=zig-0.15.0#9281f47702b57779e831d7618e158abb8eb4d4a2",
|
|
||||||
.hash = "zigini-0.3.3-36M0FRJJAADZVq5HPm-hYKMpFFTr0OgjbEYcK2ijKZ5n",
|
|
||||||
},
|
|
||||||
.termbox2 = .{
|
|
||||||
.url = "git+https://github.com/AnErrupTion/termbox2?ref=master#290ac6b8225aacfd16851224682b851b65fcb918",
|
|
||||||
.hash = "N-V-__8AAGcUBQAa5vov1Yi_9AXEffFQ1e2KsXaK4dgygRKq",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.paths = .{""},
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/sh
|
||||||
|
tar --zstd -cvf vendor.tar.zst zig-pkg ly-ui/zig-pkg ly-core/zig-pkg
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Translator = @import("translate_c").Translator;
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
const enable_x11_support = b.option(bool, "enable_x11_support", "Enable X11 support") orelse true;
|
||||||
|
const mod = b.addModule("ly-core", .{
|
||||||
|
.root_source_file = b.path("src/root.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const zigini = b.dependency("zigini", .{ .target = target, .optimize = optimize });
|
||||||
|
mod.addImport("zigini", zigini.module("zigini"));
|
||||||
|
|
||||||
|
const translate_c = b.dependency("translate_c", .{
|
||||||
|
.target = target,
|
||||||
|
});
|
||||||
|
|
||||||
|
addCImport(b, mod, translate_c, target, optimize, "pam", "#include <security/pam_appl.h>");
|
||||||
|
addCImport(b, mod, translate_c, target, optimize, "utmp", "#include <utmpx.h>");
|
||||||
|
if (enable_x11_support) {
|
||||||
|
addCImport(b, mod, translate_c, target, optimize, "xcb", "#include <xcb/xcb.h>");
|
||||||
|
}
|
||||||
|
if (target.result.os.tag == .freebsd) {
|
||||||
|
addCImport(b, mod, translate_c, target, optimize, "pwd",
|
||||||
|
\\#include <pwd.h>
|
||||||
|
\\#include <sys/types.h>
|
||||||
|
\\#include <login_cap.h>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
addCImport(b, mod, translate_c, target, optimize, "pwd", "#include <pwd.h>");
|
||||||
|
}
|
||||||
|
addCImport(b, mod, translate_c, target, optimize, "stdlib", "#include <stdlib.h>");
|
||||||
|
addCImport(b, mod, translate_c, target, optimize, "unistd", "#include <unistd.h>");
|
||||||
|
addCImport(b, mod, translate_c, target, optimize, "grp", "#include <grp.h>");
|
||||||
|
addCImport(b, mod, translate_c, target, optimize, "system_time", "#include <sys/time.h>");
|
||||||
|
addCImport(b, mod, translate_c, target, optimize, "time", "#include <time.h>");
|
||||||
|
|
||||||
|
if (target.result.os.tag == .linux) {
|
||||||
|
addCImport(b, mod, translate_c, target, optimize, "kd", "#include <sys/kd.h>");
|
||||||
|
addCImport(b, mod, translate_c, target, optimize, "vt", "#include <sys/vt.h>");
|
||||||
|
} else if (target.result.os.tag == .freebsd) {
|
||||||
|
addCImport(b, mod, translate_c, target, optimize, "kbio", "#include <sys/kbio.h>");
|
||||||
|
addCImport(b, mod, translate_c, target, optimize, "consio", "#include <sys/consio.h>");
|
||||||
|
}
|
||||||
|
|
||||||
|
const mod_tests = b.addTest(.{
|
||||||
|
.root_module = mod,
|
||||||
|
});
|
||||||
|
const run_mod_tests = b.addRunArtifact(mod_tests);
|
||||||
|
|
||||||
|
const test_step = b.step("test", "Run tests");
|
||||||
|
test_step.dependOn(&run_mod_tests.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addCImport(
|
||||||
|
b: *std.Build,
|
||||||
|
mod: *std.Build.Module,
|
||||||
|
translate_c: *std.Build.Dependency,
|
||||||
|
target: std.Build.ResolvedTarget,
|
||||||
|
optimize: std.builtin.OptimizeMode,
|
||||||
|
comptime name: []const u8,
|
||||||
|
comptime bytes: []const u8,
|
||||||
|
) void {
|
||||||
|
const pam: Translator = .init(translate_c, .{
|
||||||
|
.c_source_file = b.addWriteFiles().add(name ++ ".h", bytes),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
mod.addImport(name, pam.mod);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
.{
|
||||||
|
.name = .ly_core,
|
||||||
|
.version = "1.1.0",
|
||||||
|
.fingerprint = 0xddda7afda795472,
|
||||||
|
.minimum_zig_version = "0.16.0",
|
||||||
|
.dependencies = .{
|
||||||
|
.zigini = .{
|
||||||
|
.url = "git+https://github.com/AshAmetrine/zigini?ref=master#a665d081dda42664a96da2840ea09c5ccf9d0692",
|
||||||
|
.hash = "zigini-0.5.0-BSkB7e9WAACfyCBABNZiWL3gFMw18GKn3qBcPs8L1Ec1",
|
||||||
|
},
|
||||||
|
.translate_c = .{
|
||||||
|
.url = "git+https://codeberg.org/ziglang/translate-c#7a1a9fdc4ab00835748a6657ecbb835e3d5d45f7",
|
||||||
|
.hash = "translate_c-0.0.0-Q_BUWvP1BgCjAk6PWv5286tOlvzD9-X-NkuTzh0KxY0Q",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const interop = @import("interop.zig");
|
||||||
|
|
||||||
|
const LogFile = @This();
|
||||||
|
|
||||||
|
maybe_path: ?[]const u8,
|
||||||
|
could_open_log_file: bool = undefined,
|
||||||
|
maybe_file: ?std.Io.File = null,
|
||||||
|
buffer: []u8,
|
||||||
|
maybe_file_writer: ?std.Io.File.Writer = null,
|
||||||
|
|
||||||
|
pub fn init(io: std.Io, path: ?[]const u8, buffer: []u8) !LogFile {
|
||||||
|
var log_file = LogFile{
|
||||||
|
.maybe_path = path,
|
||||||
|
.buffer = buffer,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (path) |p| {
|
||||||
|
log_file.could_open_log_file = try openLogFile(io, p, &log_file);
|
||||||
|
} else {
|
||||||
|
std.posix.system.openlog("ly", 0, 0);
|
||||||
|
log_file.could_open_log_file = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return log_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reinit(self: *LogFile, io: std.Io) !void {
|
||||||
|
if (self.maybe_path) |path| {
|
||||||
|
self.could_open_log_file = try openLogFile(io, path, self);
|
||||||
|
} else {
|
||||||
|
std.posix.system.openlog("ly", 0, 0);
|
||||||
|
self.could_open_log_file = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *LogFile, io: std.Io) void {
|
||||||
|
if (self.maybe_file) |file| {
|
||||||
|
file.close(io);
|
||||||
|
} else {
|
||||||
|
std.posix.system.closelog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn info(self: *LogFile, io: std.Io, category: []const u8, comptime message: []const u8, args: anytype) !void {
|
||||||
|
if (self.maybe_file_writer) |*writer| {
|
||||||
|
var buffer: [128:0]u8 = undefined;
|
||||||
|
const time = interop.timeAsString(io, &buffer, "%Y-%m-%d %H:%M:%S");
|
||||||
|
|
||||||
|
try writer.interface.print("{s} [info/{s}] ", .{ time, category });
|
||||||
|
try writer.interface.print(message, args);
|
||||||
|
try writer.interface.writeByte('\n');
|
||||||
|
try writer.interface.flush();
|
||||||
|
} else {
|
||||||
|
var buffer: [1024]u8 = undefined;
|
||||||
|
const slice = try std.fmt.bufPrint(&buffer, message, args);
|
||||||
|
const msg = try std.fmt.bufPrintZ(buffer[slice.len..], "[info/{s}] {s}", .{ category, slice });
|
||||||
|
|
||||||
|
std.posix.system.syslog(std.posix.LOG.INFO, msg.ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn err(self: *LogFile, io: std.Io, category: []const u8, comptime message: []const u8, args: anytype) !void {
|
||||||
|
if (self.maybe_file_writer) |*writer| {
|
||||||
|
var buffer: [128:0]u8 = undefined;
|
||||||
|
const time = interop.timeAsString(io, &buffer, "%Y-%m-%d %H:%M:%S");
|
||||||
|
|
||||||
|
try writer.interface.print("{s} [err/{s}] ", .{ time, category });
|
||||||
|
try writer.interface.print(message, args);
|
||||||
|
try writer.interface.writeByte('\n');
|
||||||
|
try writer.interface.flush();
|
||||||
|
} else {
|
||||||
|
var buffer: [1024]u8 = undefined;
|
||||||
|
const slice = try std.fmt.bufPrint(&buffer, message, args);
|
||||||
|
const msg = try std.fmt.bufPrintZ(buffer[slice.len..], "[info/{s}] {s}", .{ category, slice });
|
||||||
|
|
||||||
|
std.posix.system.syslog(std.posix.LOG.ERR, msg.ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn openLogFile(io: std.Io, path: []const u8, log_file: *LogFile) !bool {
|
||||||
|
var could_open_log_file = true;
|
||||||
|
open_log_file: {
|
||||||
|
log_file.maybe_file = std.Io.Dir.cwd().openFile(io, path, .{ .mode = .write_only }) catch std.Io.Dir.cwd().createFile(io, path, .{ .permissions = .fromMode(0o666) }) catch {
|
||||||
|
// If we could neither open an existing log file nor create a new
|
||||||
|
// one, abort.
|
||||||
|
could_open_log_file = false;
|
||||||
|
break :open_log_file;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!could_open_log_file) {
|
||||||
|
log_file.maybe_file = try std.Io.Dir.openFileAbsolute(io, "/dev/null", .{ .mode = .write_only });
|
||||||
|
}
|
||||||
|
|
||||||
|
var log_file_writer = log_file.maybe_file.?.writer(io, log_file.buffer);
|
||||||
|
|
||||||
|
// Seek to the end of the log file
|
||||||
|
if (could_open_log_file) {
|
||||||
|
const stat = try log_file.maybe_file.?.stat(io);
|
||||||
|
try log_file_writer.seekTo(stat.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_file.maybe_file_writer = log_file_writer;
|
||||||
|
return could_open_log_file;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const ErrInt = std.meta.Int(.unsigned, @bitSizeOf(anyerror));
|
||||||
|
const PaddingInt = std.meta.Int(.unsigned, 8 - (@bitSizeOf(ErrInt) + @bitSizeOf(bool)) % 8);
|
||||||
|
|
||||||
|
const ErrorHandler = packed struct {
|
||||||
|
has_error: bool = false,
|
||||||
|
err_int: ErrInt = 0,
|
||||||
|
padding: PaddingInt = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SharedError = @This();
|
||||||
|
|
||||||
|
data: []align(std.heap.page_size_min) u8,
|
||||||
|
write_error_event_fn: ?*const fn (anyerror, *anyopaque) anyerror!void,
|
||||||
|
ctx: ?*anyopaque,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
write_error_event_fn: ?*const fn (anyerror, *anyopaque) anyerror!void,
|
||||||
|
ctx: ?*anyopaque,
|
||||||
|
) !SharedError {
|
||||||
|
const data = try std.posix.mmap(null, @sizeOf(ErrorHandler), .{ .READ = true, .WRITE = true }, .{ .TYPE = .SHARED, .ANONYMOUS = true }, -1, 0);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.data = data,
|
||||||
|
.write_error_event_fn = write_error_event_fn,
|
||||||
|
.ctx = ctx,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *SharedError) void {
|
||||||
|
std.posix.munmap(self.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeError(self: SharedError, err: anyerror) void {
|
||||||
|
var writer: std.Io.Writer = .fixed(self.data);
|
||||||
|
writer.writeStruct(ErrorHandler{ .has_error = true, .err_int = @intFromError(err) }, .native) catch {};
|
||||||
|
|
||||||
|
if (self.write_error_event_fn) |write_error_event_fn| {
|
||||||
|
@call(.auto, write_error_event_fn, .{ err, self.ctx.? }) catch {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readError(self: SharedError) ?anyerror {
|
||||||
|
var reader: std.Io.Reader = .fixed(self.data);
|
||||||
|
const err_handler = try reader.takeStruct(ErrorHandler, .native);
|
||||||
|
|
||||||
|
if (err_handler.has_error)
|
||||||
|
return @errorFromInt(err_handler.err_int);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
@ -1,51 +1,17 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const UidRange = @import("UidRange.zig");
|
const UidRange = @import("UidRange.zig");
|
||||||
|
const pwd = @import("pwd");
|
||||||
|
const stdlib = @import("stdlib");
|
||||||
|
const unistd = @import("unistd");
|
||||||
|
const grp = @import("grp");
|
||||||
|
const system_time = @import("system_time");
|
||||||
|
const time = @import("time");
|
||||||
|
|
||||||
pub const termbox = @import("termbox2");
|
pub const pam = @import("pam");
|
||||||
|
pub const utmp = @import("utmp");
|
||||||
pub const pam = @cImport({
|
|
||||||
@cInclude("security/pam_appl.h");
|
|
||||||
});
|
|
||||||
|
|
||||||
pub const utmp = @cImport({
|
|
||||||
@cInclude("utmpx.h");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Exists for X11 support only
|
// Exists for X11 support only
|
||||||
pub const xcb = @cImport({
|
pub const xcb = @import("xcb");
|
||||||
@cInclude("xcb/xcb.h");
|
|
||||||
});
|
|
||||||
|
|
||||||
const pwd = @cImport({
|
|
||||||
@cInclude("pwd.h");
|
|
||||||
// We include a FreeBSD-specific header here since login_cap.h references
|
|
||||||
// the passwd struct directly, so we can't import it separately
|
|
||||||
if (builtin.os.tag == .freebsd) {
|
|
||||||
@cInclude("sys/types.h");
|
|
||||||
@cInclude("login_cap.h");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const stdlib = @cImport({
|
|
||||||
@cInclude("stdlib.h");
|
|
||||||
});
|
|
||||||
|
|
||||||
const unistd = @cImport({
|
|
||||||
@cInclude("unistd.h");
|
|
||||||
});
|
|
||||||
|
|
||||||
const grp = @cImport({
|
|
||||||
@cInclude("grp.h");
|
|
||||||
});
|
|
||||||
|
|
||||||
const system_time = @cImport({
|
|
||||||
@cInclude("sys/time.h");
|
|
||||||
});
|
|
||||||
|
|
||||||
const time = @cImport({
|
|
||||||
@cInclude("time.h");
|
|
||||||
});
|
|
||||||
|
|
||||||
pub const TimeOfDay = struct {
|
pub const TimeOfDay = struct {
|
||||||
seconds: i64,
|
seconds: i64,
|
||||||
|
|
@ -65,13 +31,8 @@ pub const UsernameEntry = struct {
|
||||||
fn PlatformStruct() type {
|
fn PlatformStruct() type {
|
||||||
return switch (builtin.os.tag) {
|
return switch (builtin.os.tag) {
|
||||||
.linux => struct {
|
.linux => struct {
|
||||||
pub const kd = @cImport({
|
pub const kd = @import("kd");
|
||||||
@cInclude("sys/kd.h");
|
pub const vt = @import("vt");
|
||||||
});
|
|
||||||
|
|
||||||
pub const vt = @cImport({
|
|
||||||
@cInclude("sys/vt.h");
|
|
||||||
});
|
|
||||||
|
|
||||||
pub const LedState = c_char;
|
pub const LedState = c_char;
|
||||||
pub const get_led_state = kd.KDGKBLED;
|
pub const get_led_state = kd.KDGKBLED;
|
||||||
|
|
@ -81,15 +42,12 @@ fn PlatformStruct() type {
|
||||||
pub const vt_activate = vt.VT_ACTIVATE;
|
pub const vt_activate = vt.VT_ACTIVATE;
|
||||||
pub const vt_waitactive = vt.VT_WAITACTIVE;
|
pub const vt_waitactive = vt.VT_WAITACTIVE;
|
||||||
|
|
||||||
const SYSTEMD_HOMED_UID_MIN = 60001;
|
|
||||||
const SYSTEMD_HOMED_UID_MAX = 60513;
|
|
||||||
|
|
||||||
pub fn setUserContextImpl(username: [*:0]const u8, entry: UsernameEntry) !void {
|
pub fn setUserContextImpl(username: [*:0]const u8, entry: UsernameEntry) !void {
|
||||||
const status = grp.initgroups(username, @intCast(entry.gid));
|
const status = grp.initgroups(username, @intCast(entry.gid));
|
||||||
if (status != 0) return error.GroupInitializationFailed;
|
if (status != 0) return error.GroupInitializationFailed;
|
||||||
|
|
||||||
std.posix.setgid(@intCast(entry.gid)) catch return error.SetUserGidFailed;
|
if (isError(std.posix.system.setgid(@intCast(entry.gid)))) return error.SetUserGidFailed;
|
||||||
std.posix.setuid(@intCast(entry.uid)) catch return error.SetUserUidFailed;
|
if (isError(std.posix.system.setuid(@intCast(entry.uid)))) return error.SetUserUidFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Procedure:
|
// Procedure:
|
||||||
|
|
@ -101,16 +59,29 @@ fn PlatformStruct() type {
|
||||||
// 4. Finally, compare the major and minor device numbers with the
|
// 4. Finally, compare the major and minor device numbers with the
|
||||||
// extracted values. If they correspond, parse [dir] to get the
|
// extracted values. If they correspond, parse [dir] to get the
|
||||||
// TTY ID
|
// TTY ID
|
||||||
pub fn getActiveTtyImpl(allocator: std.mem.Allocator) !u8 {
|
pub fn getActiveTtyImpl(allocator: std.mem.Allocator, io: std.Io, use_kmscon_vt: bool) !u8 {
|
||||||
var file_buffer: [256]u8 = undefined;
|
var file_buffer: [256]u8 = undefined;
|
||||||
|
|
||||||
|
if (use_kmscon_vt) {
|
||||||
|
var file = try std.Io.Dir.openFileAbsolute(io, "/sys/class/tty/tty0/active", .{});
|
||||||
|
defer file.close(io);
|
||||||
|
|
||||||
|
var reader = file.reader(io, &file_buffer);
|
||||||
|
var buffer: [16]u8 = undefined;
|
||||||
|
const read = try readBuffer(&reader.interface, &buffer);
|
||||||
|
|
||||||
|
const tty = buffer[0..(read - 1)];
|
||||||
|
return std.fmt.parseInt(u8, tty["tty".len..], 10);
|
||||||
|
}
|
||||||
|
|
||||||
var tty_major: u16 = undefined;
|
var tty_major: u16 = undefined;
|
||||||
var tty_minor: u16 = undefined;
|
var tty_minor: u16 = undefined;
|
||||||
|
|
||||||
{
|
{
|
||||||
var file = try std.fs.openFileAbsolute("/proc/self/stat", .{});
|
var file = try std.Io.Dir.openFileAbsolute(io, "/proc/self/stat", .{});
|
||||||
defer file.close();
|
defer file.close(io);
|
||||||
|
|
||||||
var reader = file.reader(&file_buffer);
|
var reader = file.reader(io, &file_buffer);
|
||||||
var buffer: [1024]u8 = undefined;
|
var buffer: [1024]u8 = undefined;
|
||||||
const read = try readBuffer(&reader.interface, &buffer);
|
const read = try readBuffer(&reader.interface, &buffer);
|
||||||
|
|
||||||
|
|
@ -128,18 +99,18 @@ fn PlatformStruct() type {
|
||||||
tty_minor = tty_nr % 256;
|
tty_minor = tty_nr % 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
var directory = try std.fs.openDirAbsolute("/sys/class/tty", .{ .iterate = true });
|
var directory = try std.Io.Dir.openDirAbsolute(io, "/sys/class/tty", .{ .iterate = true });
|
||||||
defer directory.close();
|
defer directory.close(io);
|
||||||
|
|
||||||
var iterator = directory.iterate();
|
var iterator = directory.iterate();
|
||||||
while (try iterator.next()) |entry| {
|
while (try iterator.next(io)) |entry| {
|
||||||
const path = try std.fmt.allocPrint(allocator, "/sys/class/tty/{s}/dev", .{entry.name});
|
const path = try std.fmt.allocPrint(allocator, "/sys/class/tty/{s}/dev", .{entry.name});
|
||||||
defer allocator.free(path);
|
defer allocator.free(path);
|
||||||
|
|
||||||
var file = try std.fs.openFileAbsolute(path, .{});
|
var file = try std.Io.Dir.openFileAbsolute(io, path, .{});
|
||||||
defer file.close();
|
defer file.close(io);
|
||||||
|
|
||||||
var reader = file.reader(&file_buffer);
|
var reader = file.reader(io, &file_buffer);
|
||||||
var buffer: [16]u8 = undefined;
|
var buffer: [16]u8 = undefined;
|
||||||
const read = try readBuffer(&reader.interface, &buffer);
|
const read = try readBuffer(&reader.interface, &buffer);
|
||||||
|
|
||||||
|
|
@ -162,11 +133,14 @@ fn PlatformStruct() type {
|
||||||
// This is very bad parsing, but we only need to get 2 values..
|
// This is very bad parsing, but we only need to get 2 values..
|
||||||
// and the format of the file seems to be standard? So this should
|
// and the format of the file seems to be standard? So this should
|
||||||
// be fine...
|
// be fine...
|
||||||
pub fn getUserIdRange(allocator: std.mem.Allocator, file_path: []const u8) !UidRange {
|
pub fn getUserIdRange(allocator: std.mem.Allocator, io: std.Io, file_path: []const u8) !UidRange {
|
||||||
const login_defs_file = try std.fs.cwd().openFile(file_path, .{});
|
const login_defs_file = try std.Io.Dir.cwd().openFile(io, file_path, .{});
|
||||||
defer login_defs_file.close();
|
defer login_defs_file.close(io);
|
||||||
|
|
||||||
const login_defs_buffer = try login_defs_file.readToEndAlloc(allocator, std.math.maxInt(u16));
|
var buffer: [4096]u8 = undefined;
|
||||||
|
var reader = login_defs_file.reader(io, &buffer);
|
||||||
|
|
||||||
|
const login_defs_buffer = try reader.interface.allocRemaining(allocator, .unlimited);
|
||||||
defer allocator.free(login_defs_buffer);
|
defer allocator.free(login_defs_buffer);
|
||||||
|
|
||||||
var iterator = std.mem.splitScalar(u8, login_defs_buffer, '\n');
|
var iterator = std.mem.splitScalar(u8, login_defs_buffer, '\n');
|
||||||
|
|
@ -187,19 +161,6 @@ fn PlatformStruct() type {
|
||||||
|
|
||||||
if (!nameFound) return error.UidNameNotFound;
|
if (!nameFound) return error.UidNameNotFound;
|
||||||
|
|
||||||
// This code assumes the OS has a login.defs file with UID_MIN
|
|
||||||
// and UID_MAX values defined in it, which should be the case
|
|
||||||
// for most systemd-based Linux distributions out there.
|
|
||||||
// This should be a good enough safeguard for now, as there's
|
|
||||||
// no reliable (and clean) way to check for systemd support
|
|
||||||
if (uid_range.uid_min > SYSTEMD_HOMED_UID_MIN) {
|
|
||||||
uid_range.uid_min = SYSTEMD_HOMED_UID_MIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uid_range.uid_max < SYSTEMD_HOMED_UID_MAX) {
|
|
||||||
uid_range.uid_max = SYSTEMD_HOMED_UID_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
return uid_range;
|
return uid_range;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,13 +192,8 @@ fn PlatformStruct() type {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.freebsd => struct {
|
.freebsd => struct {
|
||||||
pub const kbio = @cImport({
|
pub const kbio = @import("kbio");
|
||||||
@cInclude("sys/kbio.h");
|
pub const consio = @import("consio");
|
||||||
});
|
|
||||||
|
|
||||||
pub const consio = @cImport({
|
|
||||||
@cInclude("sys/consio.h");
|
|
||||||
});
|
|
||||||
|
|
||||||
pub const LedState = c_int;
|
pub const LedState = c_int;
|
||||||
pub const get_led_state = kbio.KDGETLED;
|
pub const get_led_state = kbio.KDGETLED;
|
||||||
|
|
@ -260,11 +216,11 @@ fn PlatformStruct() type {
|
||||||
if (result != 0) return error.SetUserUidFailed;
|
if (result != 0) return error.SetUserUidFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getActiveTtyImpl(_: std.mem.Allocator) !u8 {
|
pub fn getActiveTtyImpl(_: std.mem.Allocator, _: std.Io, _: bool) !u8 {
|
||||||
return error.FeatureUnimplemented;
|
return error.FeatureUnimplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getUserIdRange(_: std.mem.Allocator, _: []const u8) !UidRange {
|
pub fn getUserIdRange(_: std.mem.Allocator, _: std.Io, _: []const u8) !UidRange {
|
||||||
return .{
|
return .{
|
||||||
// Hardcoded default values chosen from
|
// Hardcoded default values chosen from
|
||||||
// /usr/src/usr.sbin/pw/pw_conf.c
|
// /usr/src/usr.sbin/pw/pw_conf.c
|
||||||
|
|
@ -279,12 +235,27 @@ fn PlatformStruct() type {
|
||||||
|
|
||||||
const platform_struct = PlatformStruct();
|
const platform_struct = PlatformStruct();
|
||||||
|
|
||||||
|
pub fn isError(result: anytype) bool {
|
||||||
|
if (@typeInfo(@TypeOf(result)).int.signedness == .signed) {
|
||||||
|
return result < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (@typeInfo(@TypeOf(result)).int.signedness == .unsigned) {
|
||||||
|
return switch (builtin.os.tag) {
|
||||||
|
.linux => std.os.linux.errno(result) != .SUCCESS,
|
||||||
|
else => @compileError("interop.isError() not implemented for current target!"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn supportsUnicode() bool {
|
pub fn supportsUnicode() bool {
|
||||||
return builtin.os.tag == .linux or builtin.os.tag == .freebsd;
|
return builtin.os.tag == .linux or builtin.os.tag == .freebsd;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn timeAsString(buf: [:0]u8, format: [:0]const u8) []u8 {
|
pub fn timeAsString(io: std.Io, buf: [:0]u8, format: [:0]const u8) []u8 {
|
||||||
const timer = std.time.timestamp();
|
const timer: isize = @intCast(std.Io.Timestamp.now(io, .real).toSeconds());
|
||||||
const tm_info = time.localtime(&timer);
|
const tm_info = time.localtime(&timer);
|
||||||
const len = time.strftime(buf, buf.len, format, tm_info);
|
const len = time.strftime(buf, buf.len, format, tm_info);
|
||||||
|
|
||||||
|
|
@ -303,8 +274,8 @@ pub fn getTimeOfDay() !TimeOfDay {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getActiveTty(allocator: std.mem.Allocator) !u8 {
|
pub fn getActiveTty(allocator: std.mem.Allocator, io: std.Io, use_kmscon_vt: bool) !u8 {
|
||||||
return platform_struct.getActiveTtyImpl(allocator);
|
return platform_struct.getActiveTtyImpl(allocator, io, use_kmscon_vt);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn switchTty(tty: u8) !void {
|
pub fn switchTty(tty: u8) !void {
|
||||||
|
|
@ -407,6 +378,6 @@ pub fn closePasswordDatabase() void {
|
||||||
|
|
||||||
// This is very bad parsing, but we only need to get 2 values... and the format
|
// This is very bad parsing, but we only need to get 2 values... and the format
|
||||||
// of the file doesn't seem to be standard? So this should be fine...
|
// of the file doesn't seem to be standard? So this should be fine...
|
||||||
pub fn getUserIdRange(allocator: std.mem.Allocator, file_path: []const u8) !UidRange {
|
pub fn getUserIdRange(allocator: std.mem.Allocator, io: std.Io, file_path: []const u8) !UidRange {
|
||||||
return platform_struct.getUserIdRange(allocator, file_path);
|
return platform_struct.getUserIdRange(allocator, io, file_path);
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const ini = @import("zigini");
|
||||||
|
|
||||||
|
pub const interop = @import("interop.zig");
|
||||||
|
pub const UidRange = @import("UidRange.zig");
|
||||||
|
pub const LogFile = @import("LogFile.zig");
|
||||||
|
pub const SharedError = @import("SharedError.zig");
|
||||||
|
|
||||||
|
pub fn IniParser(comptime Struct: type) type {
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
const temporary_allocator = std.heap.page_allocator;
|
||||||
|
|
||||||
|
pub const Error = struct {
|
||||||
|
type_name: []const u8,
|
||||||
|
key: []const u8,
|
||||||
|
value: []const u8,
|
||||||
|
error_name: []const u8,
|
||||||
|
};
|
||||||
|
pub var global_errors: std.ArrayList(Error) = .empty;
|
||||||
|
|
||||||
|
ini_struct: ini.Ini(Struct),
|
||||||
|
structure: Struct,
|
||||||
|
maybe_load_error: ?anyerror,
|
||||||
|
errors: std.ArrayList(Error),
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
io: std.Io,
|
||||||
|
path: []const u8,
|
||||||
|
field_handler: ?fn (allocator: std.mem.Allocator, field: ini.IniField) ?ini.IniField,
|
||||||
|
) !Self {
|
||||||
|
var ini_struct = ini.Ini(Struct).init(allocator);
|
||||||
|
errdefer ini_struct.deinit();
|
||||||
|
|
||||||
|
var maybe_load_error: ?anyerror = null;
|
||||||
|
|
||||||
|
const structure = ini_struct.readFileToStruct(io, path, .{
|
||||||
|
.fieldHandler = field_handler,
|
||||||
|
.errorHandler = errorHandler,
|
||||||
|
.comment_characters = "#",
|
||||||
|
}) catch |err| load_error: {
|
||||||
|
maybe_load_error = err;
|
||||||
|
break :load_error Struct{};
|
||||||
|
};
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.ini_struct = ini_struct,
|
||||||
|
.structure = structure,
|
||||||
|
.maybe_load_error = maybe_load_error,
|
||||||
|
.errors = global_errors,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
self.ini_struct.deinit();
|
||||||
|
|
||||||
|
for (0..global_errors.items.len) |i| {
|
||||||
|
const err = global_errors.items[i];
|
||||||
|
temporary_allocator.free(err.type_name);
|
||||||
|
temporary_allocator.free(err.key);
|
||||||
|
temporary_allocator.free(err.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
global_errors.deinit(temporary_allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn errorHandler(type_name: []const u8, key: []const u8, value: []const u8, err: anyerror) void {
|
||||||
|
global_errors.append(temporary_allocator, .{
|
||||||
|
.type_name = temporary_allocator.dupe(u8, type_name) catch return,
|
||||||
|
.key = temporary_allocator.dupe(u8, key) catch return,
|
||||||
|
.value = temporary_allocator.dupe(u8, value) catch return,
|
||||||
|
.error_name = @errorName(err),
|
||||||
|
}) catch return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Translator = @import("translate_c").Translator;
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
const enable_x11_support = b.option(bool, "enable_x11_support", "Enable X11 support") orelse true;
|
||||||
|
const mod = b.addModule("ly-ui", .{
|
||||||
|
.root_source_file = b.path("src/root.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const ly_core = b.dependency("ly_core", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.enable_x11_support = enable_x11_support,
|
||||||
|
});
|
||||||
|
mod.addImport("ly-core", ly_core.module("ly-core"));
|
||||||
|
|
||||||
|
const termbox_dep = b.dependency("termbox2", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const translate_c_dep = b.dependency("translate_c", .{
|
||||||
|
.target = target,
|
||||||
|
});
|
||||||
|
|
||||||
|
const termbox2: Translator = .init(translate_c_dep, .{
|
||||||
|
.c_source_file = termbox_dep.path("termbox2.h"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
termbox2.defineCMacro("TB_IMPL", null);
|
||||||
|
// TODO 0.16.0: Workaround until Aro gets better...
|
||||||
|
// https://codeberg.org/ziglang/translate-c/issues/319
|
||||||
|
termbox2.defineCMacro("_XOPEN_SOURCE", "700");
|
||||||
|
termbox2.defineCMacro("TB_OPT_ATTR_W", "32"); // Enable 24-bit color support + styling (32-bit)
|
||||||
|
// TODO 0.16.0: Including <fcntl.h> with -OReleaseSafe causes
|
||||||
|
// __attribute__(__error__()) to be called. Below
|
||||||
|
// is the workaround.
|
||||||
|
termbox2.defineCMacro("_FORTIFY_SOURCE", "0");
|
||||||
|
// TODO 0.16.0: Needed for now
|
||||||
|
if (target.result.os.tag == .freebsd) {
|
||||||
|
termbox2.defineCMacro("__BSD_VISIBLE", "1");
|
||||||
|
}
|
||||||
|
mod.addImport("termbox2", termbox2.mod);
|
||||||
|
|
||||||
|
const mod_tests = b.addTest(.{
|
||||||
|
.root_module = mod,
|
||||||
|
});
|
||||||
|
const run_mod_tests = b.addRunArtifact(mod_tests);
|
||||||
|
|
||||||
|
const test_step = b.step("test", "Run tests");
|
||||||
|
test_step.dependOn(&run_mod_tests.step);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
.{
|
||||||
|
.name = .ly_ui,
|
||||||
|
.version = "1.1.0",
|
||||||
|
.fingerprint = 0x8d11bf85a74ec803,
|
||||||
|
.minimum_zig_version = "0.16.0",
|
||||||
|
.dependencies = .{
|
||||||
|
.ly_core = .{
|
||||||
|
.path = "../ly-core",
|
||||||
|
},
|
||||||
|
.termbox2 = .{
|
||||||
|
.url = "git+https://github.com/AnErrupTion/termbox2?ref=master#c7f241e8888ce243e1748b05c26a42fcfaaad936",
|
||||||
|
.hash = "N-V-__8AAAUXBQD6Fwpi9m0MBqWXFFaqW5l1lVrJC2Ynj7a-",
|
||||||
|
},
|
||||||
|
.translate_c = .{
|
||||||
|
.url = "git+https://codeberg.org/ziglang/translate-c#7a1a9fdc4ab00835748a6657ecbb835e3d5d45f7",
|
||||||
|
.hash = "translate_c-0.0.0-Q_BUWvP1BgCjAk6PWv5286tOlvzD9-X-NkuTzh0KxY0Q",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
const interop = @import("../interop.zig");
|
const TerminalBuffer = @import("TerminalBuffer.zig");
|
||||||
|
|
||||||
const termbox = interop.termbox;
|
|
||||||
|
|
||||||
const Cell = @This();
|
const Cell = @This();
|
||||||
|
|
||||||
|
|
@ -16,8 +14,8 @@ pub fn init(ch: u32, fg: u32, bg: u32) Cell {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put(self: Cell, x: usize, y: usize) void {
|
pub fn put(self: Cell, x: usize, y: usize) !void {
|
||||||
if (self.ch == 0) return;
|
if (self.ch == 0) return;
|
||||||
|
|
||||||
_ = termbox.tb_set_cell(@intCast(x), @intCast(y), self.ch, self.fg, self.bg);
|
try TerminalBuffer.setCell(x, y, self);
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,221 @@
|
||||||
|
const Position = @This();
|
||||||
|
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
|
||||||
|
pub fn init(x: usize, y: usize) Position {
|
||||||
|
return .{
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(self: Position, other: Position) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x + other.x,
|
||||||
|
.y = self.y + other.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addIf(self: Position, other: Position, condition: bool) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x + if (condition) other.x else 0,
|
||||||
|
.y = self.y + if (condition) other.y else 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addX(self: Position, x: usize) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x + x,
|
||||||
|
.y = self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addY(self: Position, y: usize) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x,
|
||||||
|
.y = self.y + y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addXIf(self: Position, x: usize, condition: bool) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x + if (condition) x else 0,
|
||||||
|
.y = self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addYIf(self: Position, y: usize, condition: bool) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x,
|
||||||
|
.y = self.y + if (condition) y else 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addXFrom(self: Position, other: Position) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x + other.x,
|
||||||
|
.y = self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addYFrom(self: Position, other: Position) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x,
|
||||||
|
.y = self.y + other.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addXFromIf(self: Position, other: Position, condition: bool) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x + if (condition) other.x else 0,
|
||||||
|
.y = self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addYFromIf(self: Position, other: Position, condition: bool) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x,
|
||||||
|
.y = self.y + if (condition) other.y else 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(self: Position, other: Position) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x - other.x,
|
||||||
|
.y = self.y - other.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn removeIf(self: Position, other: Position, condition: bool) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x - if (condition) other.x else 0,
|
||||||
|
.y = self.y - if (condition) other.y else 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn removeX(self: Position, x: usize) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x - x,
|
||||||
|
.y = self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn removeY(self: Position, y: usize) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x,
|
||||||
|
.y = self.y - y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn removeXIf(self: Position, x: usize, condition: bool) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x - if (condition) x else 0,
|
||||||
|
.y = self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn removeYIf(self: Position, y: usize, condition: bool) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x,
|
||||||
|
.y = self.y - if (condition) y else 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn removeXFrom(self: Position, other: Position) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x - other.x,
|
||||||
|
.y = self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn removeYFrom(self: Position, other: Position) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x,
|
||||||
|
.y = self.y - other.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn removeXFromIf(self: Position, other: Position, condition: bool) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x - if (condition) other.x else 0,
|
||||||
|
.y = self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn removeYFromIf(self: Position, other: Position, condition: bool) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x,
|
||||||
|
.y = self.y - if (condition) other.y else 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invert(self: Position, other: Position) Position {
|
||||||
|
return .{
|
||||||
|
.x = other.x - self.x,
|
||||||
|
.y = other.y - self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invertIf(self: Position, other: Position, condition: bool) Position {
|
||||||
|
return .{
|
||||||
|
.x = if (condition) other.x - self.x else self.x,
|
||||||
|
.y = if (condition) other.y - self.y else self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invertX(self: Position, width: usize) Position {
|
||||||
|
return .{
|
||||||
|
.x = width - self.x,
|
||||||
|
.y = self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invertY(self: Position, height: usize) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x,
|
||||||
|
.y = height - self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invertXIf(self: Position, width: usize, condition: bool) Position {
|
||||||
|
return .{
|
||||||
|
.x = if (condition) width - self.x else self.x,
|
||||||
|
.y = self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invertYIf(self: Position, height: usize, condition: bool) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x,
|
||||||
|
.y = if (condition) height - self.y else self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resetXFrom(self: Position, other: Position) Position {
|
||||||
|
return .{
|
||||||
|
.x = other.x,
|
||||||
|
.y = self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resetYFrom(self: Position, other: Position) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x,
|
||||||
|
.y = other.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resetXFromIf(self: Position, other: Position, condition: bool) Position {
|
||||||
|
return .{
|
||||||
|
.x = if (condition) other.x else self.x,
|
||||||
|
.y = self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resetYFromIf(self: Position, other: Position, condition: bool) Position {
|
||||||
|
return .{
|
||||||
|
.x = self.x,
|
||||||
|
.y = if (condition) other.y else self.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,679 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const Random = std.Random;
|
||||||
|
|
||||||
|
const ly_core = @import("ly-core");
|
||||||
|
const interop = ly_core.interop;
|
||||||
|
const LogFile = ly_core.LogFile;
|
||||||
|
const SharedError = ly_core.SharedError;
|
||||||
|
pub const termbox = @import("termbox2");
|
||||||
|
|
||||||
|
const Cell = @import("Cell.zig");
|
||||||
|
const keyboard = @import("keyboard.zig");
|
||||||
|
const Position = @import("Position.zig");
|
||||||
|
const Widget = @import("Widget.zig");
|
||||||
|
|
||||||
|
const TerminalBuffer = @This();
|
||||||
|
|
||||||
|
pub const KeybindCallbackFn = *const fn (*anyopaque) anyerror!bool;
|
||||||
|
pub const KeybindMap = std.AutoHashMap(keyboard.Key, struct {
|
||||||
|
callback: KeybindCallbackFn,
|
||||||
|
context: *anyopaque,
|
||||||
|
});
|
||||||
|
|
||||||
|
pub const InitOptions = struct {
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
border_fg: u32,
|
||||||
|
full_color: bool,
|
||||||
|
is_tty: bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Styling = struct {
|
||||||
|
pub const BOLD = termbox.TB_BOLD;
|
||||||
|
pub const UNDERLINE = termbox.TB_UNDERLINE;
|
||||||
|
pub const REVERSE = termbox.TB_REVERSE;
|
||||||
|
pub const ITALIC = termbox.TB_ITALIC;
|
||||||
|
pub const BLINK = termbox.TB_BLINK;
|
||||||
|
pub const HI_BLACK = termbox.TB_HI_BLACK;
|
||||||
|
pub const BRIGHT = termbox.TB_BRIGHT;
|
||||||
|
pub const DIM = termbox.TB_DIM;
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Color = struct {
|
||||||
|
pub const DEFAULT = 0x00000000;
|
||||||
|
pub const TRUE_BLACK = Styling.HI_BLACK;
|
||||||
|
pub const TRUE_RED = 0x00FF0000;
|
||||||
|
pub const TRUE_GREEN = 0x0000FF00;
|
||||||
|
pub const TRUE_YELLOW = 0x00FFFF00;
|
||||||
|
pub const TRUE_BLUE = 0x000000FF;
|
||||||
|
pub const TRUE_MAGENTA = 0x00FF00FF;
|
||||||
|
pub const TRUE_CYAN = 0x0000FFFF;
|
||||||
|
pub const TRUE_WHITE = 0x00FFFFFF;
|
||||||
|
pub const TRUE_DIM_RED = 0x00800000;
|
||||||
|
pub const TRUE_DIM_GREEN = 0x00008000;
|
||||||
|
pub const TRUE_DIM_YELLOW = 0x00808000;
|
||||||
|
pub const TRUE_DIM_BLUE = 0x00000080;
|
||||||
|
pub const TRUE_DIM_MAGENTA = 0x00800080;
|
||||||
|
pub const TRUE_DIM_CYAN = 0x00008080;
|
||||||
|
pub const TRUE_DIM_WHITE = 0x00C0C0C0;
|
||||||
|
pub const ECOL_BLACK = 1;
|
||||||
|
pub const ECOL_RED = 2;
|
||||||
|
pub const ECOL_GREEN = 3;
|
||||||
|
pub const ECOL_YELLOW = 4;
|
||||||
|
pub const ECOL_BLUE = 5;
|
||||||
|
pub const ECOL_MAGENTA = 6;
|
||||||
|
pub const ECOL_CYAN = 7;
|
||||||
|
pub const ECOL_WHITE = 8;
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const START_POSITION = Position.init(0, 0);
|
||||||
|
|
||||||
|
log_file: *LogFile,
|
||||||
|
random: Random,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
border_fg: u32,
|
||||||
|
box_chars: struct {
|
||||||
|
left_up: u32,
|
||||||
|
left_down: u32,
|
||||||
|
right_up: u32,
|
||||||
|
right_down: u32,
|
||||||
|
top: u32,
|
||||||
|
bottom: u32,
|
||||||
|
left: u32,
|
||||||
|
right: u32,
|
||||||
|
},
|
||||||
|
blank_cell: Cell,
|
||||||
|
full_color: bool,
|
||||||
|
termios: ?std.posix.termios,
|
||||||
|
keybinds: KeybindMap,
|
||||||
|
handlable_widgets: std.ArrayList(*Widget),
|
||||||
|
run: bool,
|
||||||
|
update: bool,
|
||||||
|
active_widget_index: usize,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
allocator: Allocator,
|
||||||
|
io: std.Io,
|
||||||
|
options: InitOptions,
|
||||||
|
log_file: *LogFile,
|
||||||
|
random: Random,
|
||||||
|
) !TerminalBuffer {
|
||||||
|
// Initialize termbox
|
||||||
|
var err = termbox.tb_init();
|
||||||
|
if (err != 0) {
|
||||||
|
try log_file.err(
|
||||||
|
io,
|
||||||
|
"tui",
|
||||||
|
"failed to initialise termbox2: {s}, term: {s}",
|
||||||
|
.{ termbox.tb_strerror(err), std.c.getenv("TERM").? },
|
||||||
|
);
|
||||||
|
return error.TermboxInitFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.full_color) {
|
||||||
|
err = termbox.tb_set_output_mode(termbox.TB_OUTPUT_TRUECOLOR);
|
||||||
|
if (err != 0) {
|
||||||
|
try log_file.err(
|
||||||
|
io,
|
||||||
|
"tui",
|
||||||
|
"failed to set termbox2 output mode to 24-bit color: {s}",
|
||||||
|
.{termbox.tb_strerror(err)},
|
||||||
|
);
|
||||||
|
return error.TermboxSetOutputModeFailed;
|
||||||
|
}
|
||||||
|
try log_file.info(
|
||||||
|
io,
|
||||||
|
"tui",
|
||||||
|
"termbox2 set to 24-bit color output mode",
|
||||||
|
.{},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
try log_file.info(
|
||||||
|
io,
|
||||||
|
"tui",
|
||||||
|
"termbox2 set to eight-color output mode",
|
||||||
|
.{},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's take some precautions here and clear the back buffer as well
|
||||||
|
try clearScreen(true);
|
||||||
|
|
||||||
|
const width = getWidth();
|
||||||
|
const height = getHeight();
|
||||||
|
|
||||||
|
try log_file.info(
|
||||||
|
io,
|
||||||
|
"tui",
|
||||||
|
"screen resolution is {d}x{d}",
|
||||||
|
.{ width, height },
|
||||||
|
);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.log_file = log_file,
|
||||||
|
.random = random,
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
.fg = options.fg,
|
||||||
|
.bg = options.bg,
|
||||||
|
.border_fg = options.border_fg,
|
||||||
|
.box_chars = if (interop.supportsUnicode()) .{
|
||||||
|
.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 = '|',
|
||||||
|
},
|
||||||
|
.blank_cell = Cell.init(' ', options.fg, options.bg),
|
||||||
|
.full_color = options.full_color,
|
||||||
|
// Needed to reclaim the TTY after giving up its control
|
||||||
|
.termios = try std.posix.tcgetattr(std.posix.STDIN_FILENO),
|
||||||
|
.keybinds = KeybindMap.init(allocator),
|
||||||
|
.handlable_widgets = .empty,
|
||||||
|
.run = true,
|
||||||
|
.update = true,
|
||||||
|
.active_widget_index = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *TerminalBuffer) void {
|
||||||
|
self.keybinds.deinit();
|
||||||
|
TerminalBuffer.shutdown() catch {};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn runEventLoop(
|
||||||
|
self: *TerminalBuffer,
|
||||||
|
allocator: Allocator,
|
||||||
|
io: std.Io,
|
||||||
|
shared_error: SharedError,
|
||||||
|
layers: [][]*Widget,
|
||||||
|
active_widget: *Widget,
|
||||||
|
inactivity_delay: u16,
|
||||||
|
position_widgets_fn: *const fn (*anyopaque) anyerror!void,
|
||||||
|
inactivity_event_fn: ?*const fn (*anyopaque) anyerror!void,
|
||||||
|
context: *anyopaque,
|
||||||
|
) !void {
|
||||||
|
try self.registerGlobalKeybind(io, "Ctrl+K", &moveCursorUp, self);
|
||||||
|
try self.registerGlobalKeybind(io, "Up", &moveCursorUp, self);
|
||||||
|
|
||||||
|
try self.registerGlobalKeybind(io, "Ctrl+J", &moveCursorDown, self);
|
||||||
|
try self.registerGlobalKeybind(io, "Down", &moveCursorDown, self);
|
||||||
|
|
||||||
|
try self.registerGlobalKeybind(io, "Tab", &wrapCursor, self);
|
||||||
|
try self.registerGlobalKeybind(io, "Shift+Tab", &wrapCursorReverse, self);
|
||||||
|
|
||||||
|
defer self.handlable_widgets.deinit(allocator);
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
for (layers) |layer| {
|
||||||
|
for (layer) |widget| {
|
||||||
|
try widget.update(context);
|
||||||
|
|
||||||
|
if (widget.vtable.handle_fn != null) {
|
||||||
|
try self.handlable_widgets.append(allocator, widget);
|
||||||
|
|
||||||
|
if (widget.id == active_widget.id) self.active_widget_index = i;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try @call(.auto, position_widgets_fn, .{context});
|
||||||
|
|
||||||
|
var event: termbox.tb_event = undefined;
|
||||||
|
var inactivity_cmd_ran = false;
|
||||||
|
var inactivity_time_start = try interop.getTimeOfDay();
|
||||||
|
|
||||||
|
while (self.run) {
|
||||||
|
var maybe_timeout: ?usize = null;
|
||||||
|
|
||||||
|
if (self.update) {
|
||||||
|
try TerminalBuffer.clearScreen(false);
|
||||||
|
|
||||||
|
// Reset cursor
|
||||||
|
const current_widget = self.getActiveWidget();
|
||||||
|
current_widget.handle(null) catch |err| {
|
||||||
|
shared_error.writeError(error.SetCursorFailed);
|
||||||
|
try self.log_file.err(
|
||||||
|
io,
|
||||||
|
"tui",
|
||||||
|
"failed to set cursor in active widget '{s}': {s}",
|
||||||
|
.{ current_widget.display_name, @errorName(err) },
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (layers) |layer| {
|
||||||
|
for (layer) |widget| {
|
||||||
|
try widget.update(context);
|
||||||
|
widget.draw();
|
||||||
|
|
||||||
|
if (try widget.calculateTimeout(context)) |widget_timeout| {
|
||||||
|
if (maybe_timeout == null or widget_timeout < maybe_timeout.?) maybe_timeout = widget_timeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try TerminalBuffer.presentBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inactivity_event_fn) |inactivity_fn| {
|
||||||
|
const time = try interop.getTimeOfDay();
|
||||||
|
|
||||||
|
if (!inactivity_cmd_ran and time.seconds - inactivity_time_start.seconds > inactivity_delay) {
|
||||||
|
try @call(.auto, inactivity_fn, .{context});
|
||||||
|
inactivity_cmd_ran = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const event_error = if (maybe_timeout) |timeout| termbox.tb_peek_event(&event, @intCast(timeout)) else termbox.tb_poll_event(&event);
|
||||||
|
|
||||||
|
self.update = maybe_timeout != null or event_error >= 0;
|
||||||
|
|
||||||
|
if (event_error < 0) continue;
|
||||||
|
|
||||||
|
// Input of some kind was detected, so reset the inactivity timer
|
||||||
|
inactivity_time_start = try interop.getTimeOfDay();
|
||||||
|
|
||||||
|
if (event.type == termbox.TB_EVENT_RESIZE) {
|
||||||
|
self.width = TerminalBuffer.getWidth();
|
||||||
|
self.height = TerminalBuffer.getHeight();
|
||||||
|
|
||||||
|
try self.log_file.info(
|
||||||
|
io,
|
||||||
|
"tui",
|
||||||
|
"screen resolution updated to {d}x{d}",
|
||||||
|
.{ self.width, self.height },
|
||||||
|
);
|
||||||
|
|
||||||
|
for (layers) |layer| {
|
||||||
|
for (layer) |widget| {
|
||||||
|
widget.realloc() catch |err| {
|
||||||
|
shared_error.writeError(error.WidgetReallocationFailed);
|
||||||
|
try self.log_file.err(
|
||||||
|
io,
|
||||||
|
"tui",
|
||||||
|
"failed to reallocate widget '{s}': {s}",
|
||||||
|
.{ widget.display_name, @errorName(err) },
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try @call(.auto, position_widgets_fn, .{context});
|
||||||
|
|
||||||
|
self.update = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var maybe_keys = try self.handleKeybind(allocator, event);
|
||||||
|
if (maybe_keys) |*keys| {
|
||||||
|
defer keys.deinit(allocator);
|
||||||
|
|
||||||
|
const current_widget = self.getActiveWidget();
|
||||||
|
for (keys.items) |key| {
|
||||||
|
current_widget.handle(key) catch |err| {
|
||||||
|
shared_error.writeError(error.CurrentWidgetHandlingFailed);
|
||||||
|
try self.log_file.err(
|
||||||
|
io,
|
||||||
|
"tui",
|
||||||
|
"failed to handle active widget '{s}': {s}",
|
||||||
|
.{ current_widget.display_name, @errorName(err) },
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stopEventLoop(self: *TerminalBuffer) void {
|
||||||
|
self.run = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawNextFrame(self: *TerminalBuffer, value: bool) void {
|
||||||
|
self.update = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getActiveWidget(self: *TerminalBuffer) *Widget {
|
||||||
|
return self.handlable_widgets.items[self.active_widget_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setActiveWidget(self: *TerminalBuffer, widget: *Widget) void {
|
||||||
|
for (self.handlable_widgets.items, 0..) |widg, i| {
|
||||||
|
if (widg.id == widget.id) self.active_widget_index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getWidth() usize {
|
||||||
|
return @intCast(termbox.tb_width());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getHeight() usize {
|
||||||
|
return @intCast(termbox.tb_height());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setCursor(x: usize, y: usize) !void {
|
||||||
|
if (termbox.tb_set_cursor(@intCast(x), @intCast(y)) != 0) {
|
||||||
|
return error.TermboxSetCursorFailed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clearScreen(clear_back_buffer: bool) !void {
|
||||||
|
if (termbox.tb_clear() != 0) return error.TermboxClearFailed;
|
||||||
|
if (clear_back_buffer) try clearBackBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shutdown() !void {
|
||||||
|
if (termbox.tb_shutdown() != 0) return error.TermboxShutdownFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn presentBuffer() !void {
|
||||||
|
if (termbox.tb_present() != 0) return error.TermboxPresentFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getCell(x: usize, y: usize) ?Cell {
|
||||||
|
var maybe_cell: ?*termbox.tb_cell = undefined;
|
||||||
|
if (termbox.tb_get_cell(
|
||||||
|
@intCast(x),
|
||||||
|
@intCast(y),
|
||||||
|
1,
|
||||||
|
&maybe_cell,
|
||||||
|
) != 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maybe_cell) |cell| {
|
||||||
|
return Cell.init(cell.ch, cell.fg, cell.bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setCell(x: usize, y: usize, cell: Cell) !void {
|
||||||
|
if (termbox.tb_set_cell(
|
||||||
|
@intCast(x),
|
||||||
|
@intCast(y),
|
||||||
|
cell.ch,
|
||||||
|
cell.fg,
|
||||||
|
cell.bg,
|
||||||
|
) != 0) {
|
||||||
|
return error.TermboxSetCellFailed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setCellBoundsChecked(self: *TerminalBuffer, x: isize, y: isize, cell: Cell) !void {
|
||||||
|
if (0 <= x and x < self.width and 0 <= y and y < self.height) {
|
||||||
|
try cell.put(@intCast(x), @intCast(y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reclaim(self: TerminalBuffer) !void {
|
||||||
|
if (self.termios) |termios| {
|
||||||
|
// Take back control of the TTY
|
||||||
|
const err = termbox.tb_init();
|
||||||
|
if (err != 0 and err != termbox.TB_ERR_INIT_ALREADY) return error.TermboxReinitFailed;
|
||||||
|
|
||||||
|
if (self.full_color and termbox.tb_set_output_mode(termbox.TB_OUTPUT_TRUECOLOR) != 0) {
|
||||||
|
return error.TermboxSetOutputModeFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, termios);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn registerKeybind(
|
||||||
|
self: *TerminalBuffer,
|
||||||
|
io: std.Io,
|
||||||
|
keybinds: *KeybindMap,
|
||||||
|
keybind: []const u8,
|
||||||
|
callback: KeybindCallbackFn,
|
||||||
|
context: *anyopaque,
|
||||||
|
) !void {
|
||||||
|
const key = try self.parseKeybind(io, keybind);
|
||||||
|
|
||||||
|
keybinds.put(key, .{
|
||||||
|
.callback = callback,
|
||||||
|
.context = context,
|
||||||
|
}) catch |err| {
|
||||||
|
try self.log_file.err(
|
||||||
|
io,
|
||||||
|
"tui",
|
||||||
|
"failed to register keybind {s}: {s}",
|
||||||
|
.{ keybind, @errorName(err) },
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn registerGlobalKeybind(
|
||||||
|
self: *TerminalBuffer,
|
||||||
|
io: std.Io,
|
||||||
|
keybind: []const u8,
|
||||||
|
callback: KeybindCallbackFn,
|
||||||
|
context: *anyopaque,
|
||||||
|
) !void {
|
||||||
|
try self.registerKeybind(io, &self.keybinds, keybind, callback, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn simulateKeybind(self: *TerminalBuffer, io: std.Io, keybind: []const u8) !bool {
|
||||||
|
const key = try self.parseKeybind(io, keybind);
|
||||||
|
|
||||||
|
if (self.keybinds.get(key)) |binding| {
|
||||||
|
return try @call(
|
||||||
|
.auto,
|
||||||
|
binding.callback,
|
||||||
|
.{binding.context},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const current_widget = self.getActiveWidget();
|
||||||
|
if (current_widget.keybinds) |keybinds| {
|
||||||
|
if (keybinds.get(key)) |binding| {
|
||||||
|
return try @call(
|
||||||
|
.auto,
|
||||||
|
binding.callback,
|
||||||
|
.{binding.context},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawText(
|
||||||
|
text: []const u8,
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
) !void {
|
||||||
|
const utf8view = std.unicode.Utf8View.init(text) catch return;
|
||||||
|
var utf8 = utf8view.iterator();
|
||||||
|
|
||||||
|
var i = x;
|
||||||
|
while (utf8.nextCodepoint()) |codepoint| : (i += @intCast(termbox.tb_wcwidth(codepoint))) {
|
||||||
|
const cell = Cell.init(codepoint, fg, bg);
|
||||||
|
try cell.put(i, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawConfinedText(
|
||||||
|
text: []const u8,
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
max_length: usize,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
) !void {
|
||||||
|
const utf8view = std.unicode.Utf8View.init(text) catch return;
|
||||||
|
var utf8 = utf8view.iterator();
|
||||||
|
|
||||||
|
var i = x;
|
||||||
|
while (utf8.nextCodepoint()) |codepoint| : (i += @intCast(termbox.tb_wcwidth(codepoint))) {
|
||||||
|
if (i - x >= max_length) break;
|
||||||
|
|
||||||
|
const cell = Cell.init(codepoint, fg, bg);
|
||||||
|
try cell.put(i, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawCharMultiple(
|
||||||
|
char: u32,
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
length: usize,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
) !void {
|
||||||
|
const cell = Cell.init(char, fg, bg);
|
||||||
|
for (0..length) |xx| try cell.put(x + xx, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Every codepoint is assumed to have a width of 1.
|
||||||
|
// Since Ly is normally running in a TTY, this should be fine.
|
||||||
|
pub fn strWidth(str: []const u8) usize {
|
||||||
|
const utf8view = std.unicode.Utf8View.init(str) catch return str.len;
|
||||||
|
var utf8 = utf8view.iterator();
|
||||||
|
var length: c_int = 0;
|
||||||
|
|
||||||
|
while (utf8.nextCodepoint()) |codepoint| {
|
||||||
|
length += termbox.tb_wcwidth(codepoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
return @intCast(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clearBackBuffer() !void {
|
||||||
|
if (termbox.global.initialized == 0) return;
|
||||||
|
|
||||||
|
// Clear the TTY because termbox2 doesn't seem to do it properly
|
||||||
|
const capability = termbox.global.caps[termbox.TB_CAP_CLEAR_SCREEN];
|
||||||
|
const capability_slice = std.mem.span(capability);
|
||||||
|
const result = std.posix.system.write(termbox.global.ttyfd, capability_slice.ptr, capability_slice.len);
|
||||||
|
|
||||||
|
if (result != capability_slice.len) return error.PartialClearBackBuffer;
|
||||||
|
if (result < 0) return error.ClearBackBufferFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parseKeybind(self: *TerminalBuffer, io: std.Io, keybind: []const u8) !keyboard.Key {
|
||||||
|
var key = std.mem.zeroes(keyboard.Key);
|
||||||
|
var iterator = std.mem.splitScalar(u8, keybind, '+');
|
||||||
|
|
||||||
|
while (iterator.next()) |item| {
|
||||||
|
var found = false;
|
||||||
|
|
||||||
|
inline for (std.meta.fields(keyboard.Key)) |field| {
|
||||||
|
if (std.ascii.eqlIgnoreCase(field.name, item)) {
|
||||||
|
@field(key, field.name) = true;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
try self.log_file.err(
|
||||||
|
io,
|
||||||
|
"tui",
|
||||||
|
"failed to parse key {s} of keybind {s}",
|
||||||
|
.{ item, keybind },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handleKeybind(
|
||||||
|
self: *TerminalBuffer,
|
||||||
|
allocator: Allocator,
|
||||||
|
tb_event: termbox.tb_event,
|
||||||
|
) !?std.ArrayList(keyboard.Key) {
|
||||||
|
var keys = try keyboard.getKeyList(allocator, tb_event);
|
||||||
|
|
||||||
|
for (keys.items) |key| {
|
||||||
|
if (self.keybinds.get(key)) |binding| {
|
||||||
|
const passthrough_event = try @call(
|
||||||
|
.auto,
|
||||||
|
binding.callback,
|
||||||
|
.{binding.context},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!passthrough_event) {
|
||||||
|
keys.deinit(allocator);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
const current_widget = self.getActiveWidget();
|
||||||
|
if (current_widget.keybinds) |keybinds| {
|
||||||
|
if (keybinds.get(key)) |binding| {
|
||||||
|
const passthrough_event = try @call(
|
||||||
|
.auto,
|
||||||
|
binding.callback,
|
||||||
|
.{binding.context},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!passthrough_event) {
|
||||||
|
keys.deinit(allocator);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn moveCursorUp(ptr: *anyopaque) !bool {
|
||||||
|
var state: *TerminalBuffer = @ptrCast(@alignCast(ptr));
|
||||||
|
if (state.active_widget_index == 0) return false;
|
||||||
|
|
||||||
|
state.active_widget_index -= 1;
|
||||||
|
state.update = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn moveCursorDown(ptr: *anyopaque) !bool {
|
||||||
|
var state: *TerminalBuffer = @ptrCast(@alignCast(ptr));
|
||||||
|
if (state.active_widget_index == state.handlable_widgets.items.len - 1) return false;
|
||||||
|
|
||||||
|
state.active_widget_index += 1;
|
||||||
|
state.update = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrapCursor(ptr: *anyopaque) !bool {
|
||||||
|
var state: *TerminalBuffer = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
state.active_widget_index = (state.active_widget_index + 1) % state.handlable_widgets.items.len;
|
||||||
|
state.update = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrapCursorReverse(ptr: *anyopaque) !bool {
|
||||||
|
var state: *TerminalBuffer = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
state.active_widget_index = if (state.active_widget_index == 0) state.handlable_widgets.items.len - 1 else state.active_widget_index - 1;
|
||||||
|
state.update = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,186 @@
|
||||||
|
const Widget = @This();
|
||||||
|
|
||||||
|
const keyboard = @import("keyboard.zig");
|
||||||
|
const TerminalBuffer = @import("TerminalBuffer.zig");
|
||||||
|
|
||||||
|
const VTable = struct {
|
||||||
|
deinit_fn: ?*const fn (ptr: *anyopaque) void,
|
||||||
|
realloc_fn: ?*const fn (ptr: *anyopaque) anyerror!void,
|
||||||
|
draw_fn: *const fn (ptr: *anyopaque) void,
|
||||||
|
update_fn: ?*const fn (ptr: *anyopaque, ctx: *anyopaque) anyerror!void,
|
||||||
|
handle_fn: ?*const fn (ptr: *anyopaque, maybe_key: ?keyboard.Key) anyerror!void,
|
||||||
|
calculate_timeout_fn: ?*const fn (ptr: *anyopaque, ctx: *anyopaque) anyerror!?usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub var idCounter: u64 = 0;
|
||||||
|
|
||||||
|
id: u64,
|
||||||
|
display_name: []const u8,
|
||||||
|
keybinds: ?TerminalBuffer.KeybindMap,
|
||||||
|
pointer: *anyopaque,
|
||||||
|
vtable: VTable,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
display_name: []const u8,
|
||||||
|
keybinds: ?TerminalBuffer.KeybindMap,
|
||||||
|
pointer: anytype,
|
||||||
|
comptime deinit_fn: ?fn (ptr: @TypeOf(pointer)) void,
|
||||||
|
comptime realloc_fn: ?fn (ptr: @TypeOf(pointer)) anyerror!void,
|
||||||
|
comptime draw_fn: fn (ptr: @TypeOf(pointer)) void,
|
||||||
|
comptime update_fn: ?fn (ptr: @TypeOf(pointer), ctx: *anyopaque) anyerror!void,
|
||||||
|
comptime handle_fn: ?fn (ptr: @TypeOf(pointer), maybe_key: ?keyboard.Key) anyerror!void,
|
||||||
|
comptime calculate_timeout_fn: ?fn (ptr: @TypeOf(pointer), ctx: *anyopaque) anyerror!?usize,
|
||||||
|
) Widget {
|
||||||
|
const Pointer = @TypeOf(pointer);
|
||||||
|
const Impl = struct {
|
||||||
|
pub fn deinitImpl(ptr: *anyopaque) void {
|
||||||
|
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
return @call(
|
||||||
|
.always_inline,
|
||||||
|
deinit_fn.?,
|
||||||
|
.{impl},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reallocImpl(ptr: *anyopaque) !void {
|
||||||
|
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
return @call(
|
||||||
|
.always_inline,
|
||||||
|
realloc_fn.?,
|
||||||
|
.{impl},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawImpl(ptr: *anyopaque) void {
|
||||||
|
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
return @call(
|
||||||
|
.always_inline,
|
||||||
|
draw_fn,
|
||||||
|
.{impl},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updateImpl(ptr: *anyopaque, ctx: *anyopaque) !void {
|
||||||
|
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
return @call(
|
||||||
|
.always_inline,
|
||||||
|
update_fn.?,
|
||||||
|
.{ impl, ctx },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handleImpl(ptr: *anyopaque, maybe_key: ?keyboard.Key) !void {
|
||||||
|
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
return @call(
|
||||||
|
.always_inline,
|
||||||
|
handle_fn.?,
|
||||||
|
.{ impl, maybe_key },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculateTimeoutImpl(ptr: *anyopaque, ctx: *anyopaque) !?usize {
|
||||||
|
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
return @call(
|
||||||
|
.always_inline,
|
||||||
|
calculate_timeout_fn.?,
|
||||||
|
.{ impl, ctx },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const vtable = VTable{
|
||||||
|
.deinit_fn = if (deinit_fn != null) deinitImpl else null,
|
||||||
|
.realloc_fn = if (realloc_fn != null) reallocImpl else null,
|
||||||
|
.draw_fn = drawImpl,
|
||||||
|
.update_fn = if (update_fn != null) updateImpl else null,
|
||||||
|
.handle_fn = if (handle_fn != null) handleImpl else null,
|
||||||
|
.calculate_timeout_fn = if (calculate_timeout_fn != null) calculateTimeoutImpl else null,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
idCounter += 1;
|
||||||
|
return .{
|
||||||
|
.id = idCounter,
|
||||||
|
.display_name = display_name,
|
||||||
|
.keybinds = keybinds,
|
||||||
|
.pointer = pointer,
|
||||||
|
.vtable = Impl.vtable,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Widget) void {
|
||||||
|
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||||
|
|
||||||
|
if (self.vtable.deinit_fn) |deinit_fn| {
|
||||||
|
return @call(
|
||||||
|
.auto,
|
||||||
|
deinit_fn,
|
||||||
|
.{impl},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn realloc(self: *Widget) !void {
|
||||||
|
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||||
|
|
||||||
|
if (self.vtable.realloc_fn) |realloc_fn| {
|
||||||
|
return @call(
|
||||||
|
.auto,
|
||||||
|
realloc_fn,
|
||||||
|
.{impl},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(self: *Widget) void {
|
||||||
|
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||||
|
|
||||||
|
@call(
|
||||||
|
.auto,
|
||||||
|
self.vtable.draw_fn,
|
||||||
|
.{impl},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(self: *Widget, ctx: *anyopaque) !void {
|
||||||
|
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||||
|
|
||||||
|
if (self.vtable.update_fn) |update_fn| {
|
||||||
|
return @call(
|
||||||
|
.auto,
|
||||||
|
update_fn,
|
||||||
|
.{ impl, ctx },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(self: *Widget, maybe_key: ?keyboard.Key) !void {
|
||||||
|
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||||
|
|
||||||
|
if (self.vtable.handle_fn) |handle_fn| {
|
||||||
|
return @call(
|
||||||
|
.auto,
|
||||||
|
handle_fn,
|
||||||
|
.{ impl, maybe_key },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculateTimeout(self: *Widget, ctx: *anyopaque) !?usize {
|
||||||
|
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||||
|
|
||||||
|
if (self.vtable.calculate_timeout_fn) |calculate_timeout_fn| {
|
||||||
|
return @call(
|
||||||
|
.auto,
|
||||||
|
calculate_timeout_fn,
|
||||||
|
.{ impl, ctx },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,237 @@
|
||||||
|
const BigLabel = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const ly_core = @import("ly-core");
|
||||||
|
const interop = ly_core.interop;
|
||||||
|
|
||||||
|
const en = @import("bigLabelLocales/en.zig");
|
||||||
|
const fa = @import("bigLabelLocales/fa.zig");
|
||||||
|
const Cell = @import("../Cell.zig");
|
||||||
|
const Position = @import("../Position.zig");
|
||||||
|
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||||
|
const Widget = @import("../Widget.zig");
|
||||||
|
|
||||||
|
pub const CHAR_WIDTH = 5;
|
||||||
|
pub const CHAR_HEIGHT = 5;
|
||||||
|
pub const CHAR_SIZE = CHAR_WIDTH * CHAR_HEIGHT;
|
||||||
|
pub const X: u32 = if (ly_core.interop.supportsUnicode()) 0x2593 else '#';
|
||||||
|
pub const O: u32 = 0;
|
||||||
|
|
||||||
|
// zig fmt: off
|
||||||
|
pub const LocaleChars = struct {
|
||||||
|
ZERO: [CHAR_SIZE]u21,
|
||||||
|
ONE: [CHAR_SIZE]u21,
|
||||||
|
TWO: [CHAR_SIZE]u21,
|
||||||
|
THREE: [CHAR_SIZE]u21,
|
||||||
|
FOUR: [CHAR_SIZE]u21,
|
||||||
|
FIVE: [CHAR_SIZE]u21,
|
||||||
|
SIX: [CHAR_SIZE]u21,
|
||||||
|
SEVEN: [CHAR_SIZE]u21,
|
||||||
|
EIGHT: [CHAR_SIZE]u21,
|
||||||
|
NINE: [CHAR_SIZE]u21,
|
||||||
|
S: [CHAR_SIZE]u21,
|
||||||
|
E: [CHAR_SIZE]u21,
|
||||||
|
P: [CHAR_SIZE]u21,
|
||||||
|
A: [CHAR_SIZE]u21,
|
||||||
|
M: [CHAR_SIZE]u21,
|
||||||
|
};
|
||||||
|
// zig fmt: on
|
||||||
|
|
||||||
|
pub const BigLabelLocale = enum {
|
||||||
|
en,
|
||||||
|
fa,
|
||||||
|
};
|
||||||
|
|
||||||
|
instance: ?Widget = null,
|
||||||
|
allocator: ?Allocator = null,
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
text: []const u8,
|
||||||
|
max_width: ?usize,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
locale: BigLabelLocale,
|
||||||
|
update_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!void,
|
||||||
|
calculate_timeout_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!?usize,
|
||||||
|
component_pos: Position,
|
||||||
|
children_pos: Position,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
text: []const u8,
|
||||||
|
max_width: ?usize,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
locale: BigLabelLocale,
|
||||||
|
update_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!void,
|
||||||
|
calculate_timeout_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!?usize,
|
||||||
|
) BigLabel {
|
||||||
|
return .{
|
||||||
|
.instance = null,
|
||||||
|
.allocator = null,
|
||||||
|
.buffer = buffer,
|
||||||
|
.text = text,
|
||||||
|
.max_width = max_width,
|
||||||
|
.fg = fg,
|
||||||
|
.bg = bg,
|
||||||
|
.locale = locale,
|
||||||
|
.update_fn = update_fn,
|
||||||
|
.calculate_timeout_fn = calculate_timeout_fn,
|
||||||
|
.component_pos = TerminalBuffer.START_POSITION,
|
||||||
|
.children_pos = TerminalBuffer.START_POSITION,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *BigLabel) void {
|
||||||
|
if (self.allocator) |allocator| allocator.free(self.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn widget(self: *BigLabel) *Widget {
|
||||||
|
if (self.instance) |*instance| return instance;
|
||||||
|
self.instance = Widget.init(
|
||||||
|
"BigLabel",
|
||||||
|
null,
|
||||||
|
self,
|
||||||
|
deinit,
|
||||||
|
null,
|
||||||
|
draw,
|
||||||
|
update,
|
||||||
|
null,
|
||||||
|
calculateTimeout,
|
||||||
|
);
|
||||||
|
return &self.instance.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setTextAlloc(
|
||||||
|
self: *BigLabel,
|
||||||
|
allocator: Allocator,
|
||||||
|
comptime fmt: []const u8,
|
||||||
|
args: anytype,
|
||||||
|
) !void {
|
||||||
|
self.text = try std.fmt.allocPrint(allocator, fmt, args);
|
||||||
|
self.allocator = allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setTextBuf(
|
||||||
|
self: *BigLabel,
|
||||||
|
buffer: []u8,
|
||||||
|
comptime fmt: []const u8,
|
||||||
|
args: anytype,
|
||||||
|
) !void {
|
||||||
|
self.text = try std.fmt.bufPrint(buffer, fmt, args);
|
||||||
|
self.allocator = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setText(self: *BigLabel, text: []const u8) void {
|
||||||
|
self.text = text;
|
||||||
|
self.allocator = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn positionX(self: *BigLabel, original_pos: Position) void {
|
||||||
|
self.component_pos = original_pos;
|
||||||
|
self.children_pos = original_pos.addX(TerminalBuffer.strWidth(self.text) * CHAR_WIDTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn positionY(self: *BigLabel, original_pos: Position) void {
|
||||||
|
self.component_pos = original_pos;
|
||||||
|
self.children_pos = original_pos.addY(CHAR_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn positionXY(self: *BigLabel, original_pos: Position) void {
|
||||||
|
self.component_pos = original_pos;
|
||||||
|
self.children_pos = Position.init(
|
||||||
|
TerminalBuffer.strWidth(self.text) * CHAR_WIDTH,
|
||||||
|
CHAR_HEIGHT,
|
||||||
|
).add(original_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn childrenPosition(self: BigLabel) Position {
|
||||||
|
return self.children_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(self: *BigLabel) void {
|
||||||
|
for (self.text, 0..) |c, i| {
|
||||||
|
const clock_cell = clockCell(
|
||||||
|
c,
|
||||||
|
self.fg,
|
||||||
|
self.bg,
|
||||||
|
self.locale,
|
||||||
|
);
|
||||||
|
|
||||||
|
alphaBlit(
|
||||||
|
self.component_pos.x + i * (CHAR_WIDTH + 1),
|
||||||
|
self.component_pos.y,
|
||||||
|
self.buffer.width,
|
||||||
|
self.buffer.height,
|
||||||
|
clock_cell,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(self: *BigLabel, context: *anyopaque) !void {
|
||||||
|
if (self.update_fn) |update_fn| {
|
||||||
|
return @call(
|
||||||
|
.auto,
|
||||||
|
update_fn,
|
||||||
|
.{ self, context },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculateTimeout(self: *BigLabel, ctx: *anyopaque) !?usize {
|
||||||
|
if (self.calculate_timeout_fn) |calculate_timeout_fn| {
|
||||||
|
return @call(
|
||||||
|
.auto,
|
||||||
|
calculate_timeout_fn,
|
||||||
|
.{ self, ctx },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clockCell(char: u8, fg: u32, bg: u32, locale: BigLabelLocale) [CHAR_SIZE]Cell {
|
||||||
|
var cells: [CHAR_SIZE]Cell = undefined;
|
||||||
|
|
||||||
|
//@divTrunc(time.microseconds, 500000) != 0)
|
||||||
|
const clock_chars = toBigNumber(char, locale);
|
||||||
|
for (0..cells.len) |i| cells[i] = Cell.init(clock_chars[i], fg, bg);
|
||||||
|
|
||||||
|
return cells;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alphaBlit(x: usize, y: usize, tb_width: usize, tb_height: usize, cells: [CHAR_SIZE]Cell) void {
|
||||||
|
if (x + CHAR_WIDTH >= tb_width or y + CHAR_HEIGHT >= tb_height) return;
|
||||||
|
|
||||||
|
for (0..CHAR_HEIGHT) |yy| {
|
||||||
|
for (0..CHAR_WIDTH) |xx| {
|
||||||
|
const cell = cells[yy * CHAR_WIDTH + xx];
|
||||||
|
cell.put(x + xx, y + yy) catch {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toBigNumber(char: u8, locale: BigLabelLocale) [CHAR_SIZE]u21 {
|
||||||
|
const locale_chars = switch (locale) {
|
||||||
|
.fa => fa.locale_chars,
|
||||||
|
.en => en.locale_chars,
|
||||||
|
};
|
||||||
|
return switch (char) {
|
||||||
|
'0' => locale_chars.ZERO,
|
||||||
|
'1' => locale_chars.ONE,
|
||||||
|
'2' => locale_chars.TWO,
|
||||||
|
'3' => locale_chars.THREE,
|
||||||
|
'4' => locale_chars.FOUR,
|
||||||
|
'5' => locale_chars.FIVE,
|
||||||
|
'6' => locale_chars.SIX,
|
||||||
|
'7' => locale_chars.SEVEN,
|
||||||
|
'8' => locale_chars.EIGHT,
|
||||||
|
'9' => locale_chars.NINE,
|
||||||
|
'p', 'P' => locale_chars.P,
|
||||||
|
'a', 'A' => locale_chars.A,
|
||||||
|
'm', 'M' => locale_chars.M,
|
||||||
|
':' => locale_chars.S,
|
||||||
|
else => locale_chars.E,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,190 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Cell = @import("../Cell.zig");
|
||||||
|
const Position = @import("../Position.zig");
|
||||||
|
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||||
|
const Widget = @import("../Widget.zig");
|
||||||
|
|
||||||
|
const Box = @This();
|
||||||
|
|
||||||
|
instance: ?Widget = null,
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
horizontal_margin: usize,
|
||||||
|
vertical_margin: usize,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
show_borders: bool,
|
||||||
|
blank_box: bool,
|
||||||
|
top_title: ?[]const u8,
|
||||||
|
bottom_title: ?[]const u8,
|
||||||
|
border_fg: u32,
|
||||||
|
title_fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
update_fn: ?*const fn (*Box, *anyopaque) anyerror!void,
|
||||||
|
left_pos: Position,
|
||||||
|
right_pos: Position,
|
||||||
|
children_pos: Position,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
horizontal_margin: usize,
|
||||||
|
vertical_margin: usize,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
show_borders: bool,
|
||||||
|
blank_box: bool,
|
||||||
|
top_title: ?[]const u8,
|
||||||
|
bottom_title: ?[]const u8,
|
||||||
|
border_fg: u32,
|
||||||
|
title_fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
update_fn: ?*const fn (*Box, *anyopaque) anyerror!void,
|
||||||
|
) Box {
|
||||||
|
return .{
|
||||||
|
.instance = null,
|
||||||
|
.buffer = buffer,
|
||||||
|
.horizontal_margin = horizontal_margin,
|
||||||
|
.vertical_margin = vertical_margin,
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
.show_borders = show_borders,
|
||||||
|
.blank_box = blank_box,
|
||||||
|
.top_title = top_title,
|
||||||
|
.bottom_title = bottom_title,
|
||||||
|
.border_fg = border_fg,
|
||||||
|
.title_fg = title_fg,
|
||||||
|
.bg = bg,
|
||||||
|
.update_fn = update_fn,
|
||||||
|
.left_pos = TerminalBuffer.START_POSITION,
|
||||||
|
.right_pos = TerminalBuffer.START_POSITION,
|
||||||
|
.children_pos = TerminalBuffer.START_POSITION,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn widget(self: *Box) *Widget {
|
||||||
|
if (self.instance) |*instance| return instance;
|
||||||
|
self.instance = Widget.init(
|
||||||
|
"Box",
|
||||||
|
null,
|
||||||
|
self,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
draw,
|
||||||
|
update,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
return &self.instance.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn positionXY(self: *Box, original_pos: Position) void {
|
||||||
|
if (self.buffer.width < 2 or self.buffer.height < 2) return;
|
||||||
|
|
||||||
|
self.left_pos = original_pos;
|
||||||
|
self.right_pos = Position.init(
|
||||||
|
@min(self.buffer.width, self.width),
|
||||||
|
@min(self.buffer.height, self.height),
|
||||||
|
).add(self.left_pos);
|
||||||
|
|
||||||
|
self.children_pos = Position.init(
|
||||||
|
self.horizontal_margin,
|
||||||
|
self.vertical_margin,
|
||||||
|
).add(self.left_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn childrenPosition(self: Box) Position {
|
||||||
|
return self.children_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(self: *Box) void {
|
||||||
|
if (self.show_borders) {
|
||||||
|
var left_up = Cell.init(
|
||||||
|
self.buffer.box_chars.left_up,
|
||||||
|
self.border_fg,
|
||||||
|
self.bg,
|
||||||
|
);
|
||||||
|
var right_up = Cell.init(
|
||||||
|
self.buffer.box_chars.right_up,
|
||||||
|
self.border_fg,
|
||||||
|
self.bg,
|
||||||
|
);
|
||||||
|
var left_down = Cell.init(
|
||||||
|
self.buffer.box_chars.left_down,
|
||||||
|
self.border_fg,
|
||||||
|
self.bg,
|
||||||
|
);
|
||||||
|
var right_down = Cell.init(
|
||||||
|
self.buffer.box_chars.right_down,
|
||||||
|
self.border_fg,
|
||||||
|
self.bg,
|
||||||
|
);
|
||||||
|
var top = Cell.init(
|
||||||
|
self.buffer.box_chars.top,
|
||||||
|
self.border_fg,
|
||||||
|
self.bg,
|
||||||
|
);
|
||||||
|
var bottom = Cell.init(
|
||||||
|
self.buffer.box_chars.bottom,
|
||||||
|
self.border_fg,
|
||||||
|
self.bg,
|
||||||
|
);
|
||||||
|
|
||||||
|
left_up.put(self.left_pos.x - 1, self.left_pos.y - 1) catch {};
|
||||||
|
right_up.put(self.right_pos.x, self.left_pos.y - 1) catch {};
|
||||||
|
left_down.put(self.left_pos.x - 1, self.right_pos.y) catch {};
|
||||||
|
right_down.put(self.right_pos.x, self.right_pos.y) catch {};
|
||||||
|
|
||||||
|
for (0..self.width) |i| {
|
||||||
|
top.put(self.left_pos.x + i, self.left_pos.y - 1) catch {};
|
||||||
|
bottom.put(self.left_pos.x + i, self.right_pos.y) catch {};
|
||||||
|
}
|
||||||
|
|
||||||
|
top.ch = self.buffer.box_chars.left;
|
||||||
|
bottom.ch = self.buffer.box_chars.right;
|
||||||
|
|
||||||
|
for (0..self.height) |i| {
|
||||||
|
top.put(self.left_pos.x - 1, self.left_pos.y + i) catch {};
|
||||||
|
bottom.put(self.right_pos.x, self.left_pos.y + i) catch {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.blank_box) {
|
||||||
|
for (0..self.height) |y| {
|
||||||
|
for (0..self.width) |x| {
|
||||||
|
self.buffer.blank_cell.put(self.left_pos.x + x, self.left_pos.y + y) catch {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.top_title) |title| {
|
||||||
|
TerminalBuffer.drawConfinedText(
|
||||||
|
title,
|
||||||
|
self.left_pos.x,
|
||||||
|
self.left_pos.y - 1,
|
||||||
|
self.width,
|
||||||
|
self.title_fg,
|
||||||
|
self.bg,
|
||||||
|
) catch {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.bottom_title) |title| {
|
||||||
|
TerminalBuffer.drawConfinedText(
|
||||||
|
title,
|
||||||
|
self.left_pos.x,
|
||||||
|
self.left_pos.y + self.height,
|
||||||
|
self.width,
|
||||||
|
self.title_fg,
|
||||||
|
self.bg,
|
||||||
|
) catch {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(self: *Box, ctx: *anyopaque) !void {
|
||||||
|
if (self.update_fn) |update_fn| {
|
||||||
|
return @call(
|
||||||
|
.auto,
|
||||||
|
update_fn,
|
||||||
|
.{ self, ctx },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
const Label = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const Cell = @import("../Cell.zig");
|
||||||
|
const Position = @import("../Position.zig");
|
||||||
|
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||||
|
const Widget = @import("../Widget.zig");
|
||||||
|
|
||||||
|
instance: ?Widget,
|
||||||
|
allocator: ?Allocator,
|
||||||
|
text: []const u8,
|
||||||
|
max_width: ?usize,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
update_fn: ?*const fn (*Label, *anyopaque) anyerror!void,
|
||||||
|
calculate_timeout_fn: ?*const fn (*Label, *anyopaque) anyerror!?usize,
|
||||||
|
component_pos: Position,
|
||||||
|
children_pos: Position,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
text: []const u8,
|
||||||
|
max_width: ?usize,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
update_fn: ?*const fn (*Label, *anyopaque) anyerror!void,
|
||||||
|
calculate_timeout_fn: ?*const fn (*Label, *anyopaque) anyerror!?usize,
|
||||||
|
) Label {
|
||||||
|
return .{
|
||||||
|
.instance = null,
|
||||||
|
.allocator = null,
|
||||||
|
.text = text,
|
||||||
|
.max_width = max_width,
|
||||||
|
.fg = fg,
|
||||||
|
.bg = bg,
|
||||||
|
.update_fn = update_fn,
|
||||||
|
.calculate_timeout_fn = calculate_timeout_fn,
|
||||||
|
.component_pos = TerminalBuffer.START_POSITION,
|
||||||
|
.children_pos = TerminalBuffer.START_POSITION,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Label) void {
|
||||||
|
if (self.allocator) |allocator| allocator.free(self.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn widget(self: *Label) *Widget {
|
||||||
|
if (self.instance) |*instance| return instance;
|
||||||
|
self.instance = Widget.init(
|
||||||
|
"Label",
|
||||||
|
null,
|
||||||
|
self,
|
||||||
|
deinit,
|
||||||
|
null,
|
||||||
|
draw,
|
||||||
|
update,
|
||||||
|
null,
|
||||||
|
calculateTimeout,
|
||||||
|
);
|
||||||
|
return &self.instance.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setTextAlloc(
|
||||||
|
self: *Label,
|
||||||
|
allocator: Allocator,
|
||||||
|
comptime fmt: []const u8,
|
||||||
|
args: anytype,
|
||||||
|
) !void {
|
||||||
|
self.text = try std.fmt.allocPrint(allocator, fmt, args);
|
||||||
|
self.allocator = allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setTextBuf(
|
||||||
|
self: *Label,
|
||||||
|
buffer: []u8,
|
||||||
|
comptime fmt: []const u8,
|
||||||
|
args: anytype,
|
||||||
|
) !void {
|
||||||
|
self.text = try std.fmt.bufPrint(buffer, fmt, args);
|
||||||
|
self.allocator = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setText(self: *Label, text: []const u8) void {
|
||||||
|
self.text = text;
|
||||||
|
self.allocator = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn positionX(self: *Label, original_pos: Position) void {
|
||||||
|
self.component_pos = original_pos;
|
||||||
|
self.children_pos = original_pos.addX(TerminalBuffer.strWidth(self.text));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn positionY(self: *Label, original_pos: Position) void {
|
||||||
|
self.component_pos = original_pos;
|
||||||
|
self.children_pos = original_pos.addY(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn positionXY(self: *Label, original_pos: Position) void {
|
||||||
|
self.component_pos = original_pos;
|
||||||
|
self.children_pos = Position.init(
|
||||||
|
TerminalBuffer.strWidth(self.text),
|
||||||
|
1,
|
||||||
|
).add(original_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn childrenPosition(self: Label) Position {
|
||||||
|
return self.children_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(self: *Label) void {
|
||||||
|
if (self.max_width) |width| {
|
||||||
|
TerminalBuffer.drawConfinedText(
|
||||||
|
self.text,
|
||||||
|
self.component_pos.x,
|
||||||
|
self.component_pos.y,
|
||||||
|
width,
|
||||||
|
self.fg,
|
||||||
|
self.bg,
|
||||||
|
) catch {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalBuffer.drawText(
|
||||||
|
self.text,
|
||||||
|
self.component_pos.x,
|
||||||
|
self.component_pos.y,
|
||||||
|
self.fg,
|
||||||
|
self.bg,
|
||||||
|
) catch {};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(self: *Label, ctx: *anyopaque) !void {
|
||||||
|
if (self.update_fn) |update_fn| {
|
||||||
|
return @call(
|
||||||
|
.auto,
|
||||||
|
update_fn,
|
||||||
|
.{ self, ctx },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculateTimeout(self: *Label, ctx: *anyopaque) !?usize {
|
||||||
|
if (self.calculate_timeout_fn) |calculate_timeout_fn| {
|
||||||
|
return @call(
|
||||||
|
.auto,
|
||||||
|
calculate_timeout_fn,
|
||||||
|
.{ self, ctx },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,246 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const keyboard = @import("../keyboard.zig");
|
||||||
|
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||||
|
const Position = @import("../Position.zig");
|
||||||
|
const Widget = @import("../Widget.zig");
|
||||||
|
|
||||||
|
const DynamicString = std.ArrayListUnmanaged(u8);
|
||||||
|
|
||||||
|
const Text = @This();
|
||||||
|
|
||||||
|
instance: ?Widget,
|
||||||
|
allocator: Allocator,
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
text: DynamicString,
|
||||||
|
end: usize,
|
||||||
|
cursor: usize,
|
||||||
|
visible_start: usize,
|
||||||
|
width: usize,
|
||||||
|
component_pos: Position,
|
||||||
|
children_pos: Position,
|
||||||
|
should_insert: bool,
|
||||||
|
masked: bool,
|
||||||
|
maybe_mask: ?u32,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
keybinds: TerminalBuffer.KeybindMap,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
allocator: Allocator,
|
||||||
|
io: std.Io,
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
should_insert: bool,
|
||||||
|
masked: bool,
|
||||||
|
maybe_mask: ?u32,
|
||||||
|
width: usize,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
) !*Text {
|
||||||
|
var self = try allocator.create(Text);
|
||||||
|
self.* = Text{
|
||||||
|
.instance = null,
|
||||||
|
.allocator = allocator,
|
||||||
|
.buffer = buffer,
|
||||||
|
.text = .empty,
|
||||||
|
.end = 0,
|
||||||
|
.cursor = 0,
|
||||||
|
.visible_start = 0,
|
||||||
|
.width = width,
|
||||||
|
.component_pos = TerminalBuffer.START_POSITION,
|
||||||
|
.children_pos = TerminalBuffer.START_POSITION,
|
||||||
|
.should_insert = should_insert,
|
||||||
|
.masked = masked,
|
||||||
|
.maybe_mask = maybe_mask,
|
||||||
|
.fg = fg,
|
||||||
|
.bg = bg,
|
||||||
|
.keybinds = .init(allocator),
|
||||||
|
};
|
||||||
|
|
||||||
|
try buffer.registerKeybind(io, &self.keybinds, "Left", &goLeft, self);
|
||||||
|
try buffer.registerKeybind(io, &self.keybinds, "Right", &goRight, self);
|
||||||
|
try buffer.registerKeybind(io, &self.keybinds, "Delete", &delete, self);
|
||||||
|
try buffer.registerKeybind(io, &self.keybinds, "Backspace", &backspace, self);
|
||||||
|
try buffer.registerKeybind(io, &self.keybinds, "Ctrl+U", &clearTextEntry, self);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Text) void {
|
||||||
|
self.text.deinit(self.allocator);
|
||||||
|
self.keybinds.deinit();
|
||||||
|
self.allocator.destroy(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn widget(self: *Text) *Widget {
|
||||||
|
if (self.instance) |*instance| return instance;
|
||||||
|
self.instance = Widget.init(
|
||||||
|
"Text",
|
||||||
|
self.keybinds,
|
||||||
|
self,
|
||||||
|
deinit,
|
||||||
|
null,
|
||||||
|
draw,
|
||||||
|
null,
|
||||||
|
handle,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
return &self.instance.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn positionX(self: *Text, original_pos: Position) void {
|
||||||
|
self.component_pos = original_pos;
|
||||||
|
self.children_pos = original_pos.addX(self.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn positionY(self: *Text, original_pos: Position) void {
|
||||||
|
self.component_pos = original_pos;
|
||||||
|
self.children_pos = original_pos.addY(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn positionXY(self: *Text, original_pos: Position) void {
|
||||||
|
self.component_pos = original_pos;
|
||||||
|
self.children_pos = Position.init(
|
||||||
|
self.width,
|
||||||
|
1,
|
||||||
|
).add(original_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn childrenPosition(self: Text) Position {
|
||||||
|
return self.children_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(self: *Text) void {
|
||||||
|
self.text.clearRetainingCapacity();
|
||||||
|
self.end = 0;
|
||||||
|
self.cursor = 0;
|
||||||
|
self.visible_start = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggleMask(self: *Text) void {
|
||||||
|
self.masked = !self.masked;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(self: *Text, maybe_key: ?keyboard.Key) !void {
|
||||||
|
if (maybe_key) |key| {
|
||||||
|
if (self.should_insert) {
|
||||||
|
const maybe_character = key.getEnabledPrintableAscii();
|
||||||
|
if (maybe_character) |character| try self.write(character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.masked and self.maybe_mask == null) {
|
||||||
|
try TerminalBuffer.setCursor(
|
||||||
|
self.component_pos.x,
|
||||||
|
self.component_pos.y,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try TerminalBuffer.setCursor(
|
||||||
|
self.component_pos.x + (self.cursor - self.visible_start),
|
||||||
|
self.component_pos.y,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(self: *Text) void {
|
||||||
|
if (self.masked) {
|
||||||
|
if (self.maybe_mask) |mask| {
|
||||||
|
if (self.width < 1) return;
|
||||||
|
|
||||||
|
const length = @min(TerminalBuffer.strWidth(self.text.items), self.width - 1);
|
||||||
|
if (length == 0) return;
|
||||||
|
|
||||||
|
TerminalBuffer.drawCharMultiple(
|
||||||
|
mask,
|
||||||
|
self.component_pos.x,
|
||||||
|
self.component_pos.y,
|
||||||
|
length,
|
||||||
|
self.fg,
|
||||||
|
self.bg,
|
||||||
|
) catch {};
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const str_length = TerminalBuffer.strWidth(self.text.items);
|
||||||
|
const length = @min(str_length, self.width);
|
||||||
|
if (length == 0) return;
|
||||||
|
|
||||||
|
const visible_slice = vs: {
|
||||||
|
if (str_length > self.width and self.cursor < str_length) {
|
||||||
|
break :vs self.text.items[self.visible_start..(self.width + self.visible_start)];
|
||||||
|
} else {
|
||||||
|
break :vs self.text.items[self.visible_start..];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TerminalBuffer.drawText(
|
||||||
|
visible_slice,
|
||||||
|
self.component_pos.x,
|
||||||
|
self.component_pos.y,
|
||||||
|
self.fg,
|
||||||
|
self.bg,
|
||||||
|
) catch {};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goLeft(ptr: *anyopaque) !bool {
|
||||||
|
var self: *Text = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
if (self.cursor == 0) return false;
|
||||||
|
if (self.visible_start > 0) self.visible_start -= 1;
|
||||||
|
|
||||||
|
self.cursor -= 1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goRight(ptr: *anyopaque) !bool {
|
||||||
|
var self: *Text = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
if (self.cursor >= self.end) return false;
|
||||||
|
if (self.cursor - self.visible_start == self.width - 1) self.visible_start += 1;
|
||||||
|
|
||||||
|
self.cursor += 1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete(ptr: *anyopaque) !bool {
|
||||||
|
var self: *Text = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
if (self.cursor >= self.end or !self.should_insert) return false;
|
||||||
|
|
||||||
|
_ = self.text.orderedRemove(self.cursor);
|
||||||
|
|
||||||
|
self.end -= 1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backspace(ptr: *anyopaque) !bool {
|
||||||
|
const self: *Text = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
if (self.cursor == 0 or !self.should_insert) return false;
|
||||||
|
|
||||||
|
_ = try goLeft(ptr);
|
||||||
|
_ = try delete(ptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(self: *Text, char: u8) !void {
|
||||||
|
if (char == 0) return;
|
||||||
|
|
||||||
|
try self.text.insert(self.allocator, self.cursor, char);
|
||||||
|
|
||||||
|
self.end += 1;
|
||||||
|
_ = try goRight(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clearTextEntry(ptr: *anyopaque) !bool {
|
||||||
|
var self: *Text = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
if (!self.should_insert) return false;
|
||||||
|
|
||||||
|
self.clear();
|
||||||
|
self.buffer.drawNextFrame(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
const Lang = @import("Lang.zig");
|
const BigLabel = @import("../BigLabel.zig");
|
||||||
|
const LocaleChars = BigLabel.LocaleChars;
|
||||||
const LocaleChars = Lang.LocaleChars;
|
const X = BigLabel.X;
|
||||||
const X = Lang.X;
|
const O = BigLabel.O;
|
||||||
const O = Lang.O;
|
|
||||||
|
|
||||||
// zig fmt: off
|
// zig fmt: off
|
||||||
pub const locale_chars = LocaleChars{
|
pub const locale_chars = LocaleChars{
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
const Lang = @import("Lang.zig");
|
const BigLabel = @import("../BigLabel.zig");
|
||||||
|
const LocaleChars = BigLabel.LocaleChars;
|
||||||
const LocaleChars = Lang.LocaleChars;
|
const X = BigLabel.X;
|
||||||
const X = Lang.X;
|
const O = BigLabel.O;
|
||||||
const O = Lang.O;
|
|
||||||
|
|
||||||
// zig fmt: off
|
// zig fmt: off
|
||||||
pub const locale_chars = LocaleChars{
|
pub const locale_chars = LocaleChars{
|
||||||
|
|
@ -0,0 +1,172 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Cell = @import("../Cell.zig");
|
||||||
|
const keyboard = @import("../keyboard.zig");
|
||||||
|
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||||
|
const Position = @import("../Position.zig");
|
||||||
|
|
||||||
|
pub fn CyclableLabel(comptime ItemType: type, comptime ChangeItemType: type) type {
|
||||||
|
return struct {
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const ItemList = std.ArrayListUnmanaged(ItemType);
|
||||||
|
const DrawItemFn = *const fn (*Self, ItemType, usize, usize, usize) void;
|
||||||
|
const ChangeItemFn = *const fn (ItemType, ?ChangeItemType) void;
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
allocator: Allocator,
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
list: ItemList,
|
||||||
|
current: usize,
|
||||||
|
width: usize,
|
||||||
|
component_pos: Position,
|
||||||
|
children_pos: Position,
|
||||||
|
text_in_center: bool,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
cursor: usize,
|
||||||
|
draw_item_fn: DrawItemFn,
|
||||||
|
change_item_fn: ?ChangeItemFn,
|
||||||
|
change_item_arg: ?ChangeItemType,
|
||||||
|
keybinds: TerminalBuffer.KeybindMap,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
allocator: Allocator,
|
||||||
|
io: std.Io,
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
draw_item_fn: DrawItemFn,
|
||||||
|
change_item_fn: ?ChangeItemFn,
|
||||||
|
change_item_arg: ?ChangeItemType,
|
||||||
|
width: usize,
|
||||||
|
text_in_center: bool,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
) !*Self {
|
||||||
|
var self = try allocator.create(Self);
|
||||||
|
self.* = .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.buffer = buffer,
|
||||||
|
.list = .empty,
|
||||||
|
.current = 0,
|
||||||
|
.width = width,
|
||||||
|
.component_pos = TerminalBuffer.START_POSITION,
|
||||||
|
.children_pos = TerminalBuffer.START_POSITION,
|
||||||
|
.text_in_center = text_in_center,
|
||||||
|
.fg = fg,
|
||||||
|
.bg = bg,
|
||||||
|
.cursor = 0,
|
||||||
|
.draw_item_fn = draw_item_fn,
|
||||||
|
.change_item_fn = change_item_fn,
|
||||||
|
.change_item_arg = change_item_arg,
|
||||||
|
.keybinds = .init(allocator),
|
||||||
|
};
|
||||||
|
|
||||||
|
try buffer.registerKeybind(io, &self.keybinds, "Left", &goLeft, self);
|
||||||
|
try buffer.registerKeybind(io, &self.keybinds, "Ctrl+H", &goLeft, self);
|
||||||
|
try buffer.registerKeybind(io, &self.keybinds, "Right", &goRight, self);
|
||||||
|
try buffer.registerKeybind(io, &self.keybinds, "Ctrl+L", &goRight, self);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
self.list.deinit(self.allocator);
|
||||||
|
self.keybinds.deinit();
|
||||||
|
self.allocator.destroy(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn positionX(self: *Self, original_pos: Position) void {
|
||||||
|
self.component_pos = original_pos;
|
||||||
|
self.cursor = self.component_pos.x + 2;
|
||||||
|
self.children_pos = original_pos.addX(self.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn positionY(self: *Self, original_pos: Position) void {
|
||||||
|
self.component_pos = original_pos;
|
||||||
|
self.cursor = self.component_pos.x + 2;
|
||||||
|
self.children_pos = original_pos.addY(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn positionXY(self: *Self, original_pos: Position) void {
|
||||||
|
self.component_pos = original_pos;
|
||||||
|
self.cursor = self.component_pos.x + 2;
|
||||||
|
self.children_pos = Position.init(
|
||||||
|
self.width,
|
||||||
|
1,
|
||||||
|
).add(original_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn childrenPosition(self: Self) Position {
|
||||||
|
return self.children_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addItem(self: *Self, item: ItemType) !void {
|
||||||
|
try self.list.append(self.allocator, item);
|
||||||
|
self.current = self.list.items.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(self: *Self, _: ?keyboard.Key) !void {
|
||||||
|
try TerminalBuffer.setCursor(
|
||||||
|
self.component_pos.x + self.cursor + 2,
|
||||||
|
self.component_pos.y,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(self: *Self) void {
|
||||||
|
if (self.list.items.len == 0) return;
|
||||||
|
if (self.width < 2) return;
|
||||||
|
|
||||||
|
var left_arrow = Cell.init('<', self.fg, self.bg);
|
||||||
|
var right_arrow = Cell.init('>', self.fg, self.bg);
|
||||||
|
|
||||||
|
left_arrow.put(self.component_pos.x, self.component_pos.y) catch {};
|
||||||
|
right_arrow.put(
|
||||||
|
self.component_pos.x + self.width - 1,
|
||||||
|
self.component_pos.y,
|
||||||
|
) catch {};
|
||||||
|
|
||||||
|
const current_item = self.list.items[self.current];
|
||||||
|
const x = self.component_pos.x + 2;
|
||||||
|
const y = self.component_pos.y;
|
||||||
|
const width = self.width - 2;
|
||||||
|
|
||||||
|
@call(
|
||||||
|
.auto,
|
||||||
|
self.draw_item_fn,
|
||||||
|
.{ self, current_item, x, y, width },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goLeft(ptr: *anyopaque) !bool {
|
||||||
|
var self: *Self = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
self.current = if (self.current == 0) self.list.items.len - 1 else self.current - 1;
|
||||||
|
|
||||||
|
if (self.change_item_fn) |change_item_fn| {
|
||||||
|
@call(
|
||||||
|
.auto,
|
||||||
|
change_item_fn,
|
||||||
|
.{ self.list.items[self.current], self.change_item_arg },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goRight(ptr: *anyopaque) !bool {
|
||||||
|
var self: *Self = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
self.current = if (self.current == self.list.items.len - 1) 0 else self.current + 1;
|
||||||
|
|
||||||
|
if (self.change_item_fn) |change_item_fn| {
|
||||||
|
@call(
|
||||||
|
.auto,
|
||||||
|
change_item_fn,
|
||||||
|
.{ self.list.items[self.current], self.change_item_arg },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,711 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const KeyList = std.ArrayList(Key);
|
||||||
|
|
||||||
|
const TerminalBuffer = @import("TerminalBuffer.zig");
|
||||||
|
const termbox = TerminalBuffer.termbox;
|
||||||
|
|
||||||
|
pub const Key = packed struct {
|
||||||
|
ctrl: bool,
|
||||||
|
shift: bool,
|
||||||
|
alt: bool,
|
||||||
|
|
||||||
|
f1: bool,
|
||||||
|
f2: bool,
|
||||||
|
f3: bool,
|
||||||
|
f4: bool,
|
||||||
|
f5: bool,
|
||||||
|
f6: bool,
|
||||||
|
f7: bool,
|
||||||
|
f8: bool,
|
||||||
|
f9: bool,
|
||||||
|
f10: bool,
|
||||||
|
f11: bool,
|
||||||
|
f12: bool,
|
||||||
|
|
||||||
|
insert: bool,
|
||||||
|
delete: bool,
|
||||||
|
home: bool,
|
||||||
|
end: bool,
|
||||||
|
pageup: bool,
|
||||||
|
pagedown: bool,
|
||||||
|
up: bool,
|
||||||
|
down: bool,
|
||||||
|
left: bool,
|
||||||
|
right: bool,
|
||||||
|
tab: bool,
|
||||||
|
backspace: bool,
|
||||||
|
enter: bool,
|
||||||
|
|
||||||
|
@" ": bool,
|
||||||
|
@"!": bool,
|
||||||
|
@"`": bool,
|
||||||
|
esc: bool,
|
||||||
|
@"[": bool,
|
||||||
|
@"\\": bool,
|
||||||
|
@"]": bool,
|
||||||
|
@"/": bool,
|
||||||
|
_: bool,
|
||||||
|
@"'": bool,
|
||||||
|
@"\"": bool,
|
||||||
|
@",": bool,
|
||||||
|
@"-": bool,
|
||||||
|
@".": bool,
|
||||||
|
@"#": bool,
|
||||||
|
@"$": bool,
|
||||||
|
@"%": bool,
|
||||||
|
@"&": bool,
|
||||||
|
@"*": bool,
|
||||||
|
@"(": bool,
|
||||||
|
@")": bool,
|
||||||
|
@"+": bool,
|
||||||
|
@"=": bool,
|
||||||
|
@":": bool,
|
||||||
|
@";": bool,
|
||||||
|
@"<": bool,
|
||||||
|
@">": bool,
|
||||||
|
@"?": bool,
|
||||||
|
@"@": bool,
|
||||||
|
@"^": bool,
|
||||||
|
@"~": bool,
|
||||||
|
@"{": bool,
|
||||||
|
@"}": bool,
|
||||||
|
@"|": bool,
|
||||||
|
|
||||||
|
@"0": bool,
|
||||||
|
@"1": bool,
|
||||||
|
@"2": bool,
|
||||||
|
@"3": bool,
|
||||||
|
@"4": bool,
|
||||||
|
@"5": bool,
|
||||||
|
@"6": bool,
|
||||||
|
@"7": bool,
|
||||||
|
@"8": bool,
|
||||||
|
@"9": bool,
|
||||||
|
|
||||||
|
a: bool,
|
||||||
|
b: bool,
|
||||||
|
c: bool,
|
||||||
|
d: bool,
|
||||||
|
e: bool,
|
||||||
|
f: bool,
|
||||||
|
g: bool,
|
||||||
|
h: bool,
|
||||||
|
i: bool,
|
||||||
|
j: bool,
|
||||||
|
k: bool,
|
||||||
|
l: bool,
|
||||||
|
m: bool,
|
||||||
|
n: bool,
|
||||||
|
o: bool,
|
||||||
|
p: bool,
|
||||||
|
q: bool,
|
||||||
|
r: bool,
|
||||||
|
s: bool,
|
||||||
|
t: bool,
|
||||||
|
u: bool,
|
||||||
|
v: bool,
|
||||||
|
w: bool,
|
||||||
|
x: bool,
|
||||||
|
y: bool,
|
||||||
|
z: bool,
|
||||||
|
|
||||||
|
pub fn getEnabledPrintableAscii(self: Key) ?u8 {
|
||||||
|
if (self.ctrl or self.alt) return null;
|
||||||
|
|
||||||
|
inline for (std.meta.fields(Key)) |field| {
|
||||||
|
if (field.name.len == 1 and std.ascii.isPrint(field.name[0]) and @field(self, field.name)) {
|
||||||
|
if (self.shift) {
|
||||||
|
if (!std.ascii.isAlphanumeric(field.name[0])) return null;
|
||||||
|
return std.ascii.toUpper(field.name[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return field.name[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn getKeyList(allocator: Allocator, tb_event: termbox.tb_event) !KeyList {
|
||||||
|
var keys: KeyList = .empty;
|
||||||
|
var key = std.mem.zeroes(Key);
|
||||||
|
|
||||||
|
if (tb_event.mod & termbox.TB_MOD_CTRL != 0) key.ctrl = true;
|
||||||
|
if (tb_event.mod & termbox.TB_MOD_SHIFT != 0) key.shift = true;
|
||||||
|
if (tb_event.mod & termbox.TB_MOD_ALT != 0) key.alt = true;
|
||||||
|
|
||||||
|
if (tb_event.key == termbox.TB_KEY_BACK_TAB) {
|
||||||
|
key.shift = true;
|
||||||
|
key.tab = true;
|
||||||
|
} else if (tb_event.key > termbox.TB_KEY_BACK_TAB) {
|
||||||
|
const code = 0xFFFF - tb_event.key;
|
||||||
|
|
||||||
|
switch (code) {
|
||||||
|
0 => key.f1 = true,
|
||||||
|
1 => key.f2 = true,
|
||||||
|
2 => key.f3 = true,
|
||||||
|
3 => key.f4 = true,
|
||||||
|
4 => key.f5 = true,
|
||||||
|
5 => key.f6 = true,
|
||||||
|
6 => key.f7 = true,
|
||||||
|
7 => key.f8 = true,
|
||||||
|
8 => key.f9 = true,
|
||||||
|
9 => key.f10 = true,
|
||||||
|
10 => key.f11 = true,
|
||||||
|
11 => key.f12 = true,
|
||||||
|
12 => key.insert = true,
|
||||||
|
13 => key.delete = true,
|
||||||
|
14 => key.home = true,
|
||||||
|
15 => key.end = true,
|
||||||
|
16 => key.pageup = true,
|
||||||
|
17 => key.pagedown = true,
|
||||||
|
18 => key.up = true,
|
||||||
|
19 => key.down = true,
|
||||||
|
20 => key.left = true,
|
||||||
|
21 => key.right = true,
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
} else if (tb_event.ch < 128) {
|
||||||
|
const code = if (tb_event.ch == 0 and tb_event.key < 128) tb_event.key else tb_event.ch;
|
||||||
|
|
||||||
|
switch (code) {
|
||||||
|
// Non-standard control codes
|
||||||
|
0 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.@"2" = true;
|
||||||
|
try keys.append(allocator, key);
|
||||||
|
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"`" = true;
|
||||||
|
},
|
||||||
|
1 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.a = true;
|
||||||
|
},
|
||||||
|
2 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.b = true;
|
||||||
|
},
|
||||||
|
3 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.c = true;
|
||||||
|
},
|
||||||
|
4 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.d = true;
|
||||||
|
},
|
||||||
|
5 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.e = true;
|
||||||
|
},
|
||||||
|
6 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.f = true;
|
||||||
|
},
|
||||||
|
7 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.g = true;
|
||||||
|
},
|
||||||
|
8 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.h = true;
|
||||||
|
try keys.append(allocator, key);
|
||||||
|
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.backspace = true;
|
||||||
|
},
|
||||||
|
9 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.i = true;
|
||||||
|
try keys.append(allocator, key);
|
||||||
|
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.tab = true;
|
||||||
|
},
|
||||||
|
10 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.j = true;
|
||||||
|
},
|
||||||
|
11 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.k = true;
|
||||||
|
},
|
||||||
|
12 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.l = true;
|
||||||
|
},
|
||||||
|
13 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.m = true;
|
||||||
|
try keys.append(allocator, key);
|
||||||
|
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.enter = true;
|
||||||
|
},
|
||||||
|
14 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.n = true;
|
||||||
|
},
|
||||||
|
15 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.o = true;
|
||||||
|
},
|
||||||
|
16 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.p = true;
|
||||||
|
},
|
||||||
|
17 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.q = true;
|
||||||
|
},
|
||||||
|
18 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.r = true;
|
||||||
|
},
|
||||||
|
19 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.s = true;
|
||||||
|
},
|
||||||
|
20 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.t = true;
|
||||||
|
},
|
||||||
|
21 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.u = true;
|
||||||
|
},
|
||||||
|
22 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.v = true;
|
||||||
|
},
|
||||||
|
23 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.w = true;
|
||||||
|
},
|
||||||
|
24 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.x = true;
|
||||||
|
},
|
||||||
|
25 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.y = true;
|
||||||
|
},
|
||||||
|
26 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.z = true;
|
||||||
|
},
|
||||||
|
27 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.@"3" = true;
|
||||||
|
try keys.append(allocator, key);
|
||||||
|
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.esc = true;
|
||||||
|
try keys.append(allocator, key);
|
||||||
|
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"[" = true;
|
||||||
|
},
|
||||||
|
28 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.@"4" = true;
|
||||||
|
try keys.append(allocator, key);
|
||||||
|
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"\\" = true;
|
||||||
|
},
|
||||||
|
29 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.@"5" = true;
|
||||||
|
try keys.append(allocator, key);
|
||||||
|
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"]" = true;
|
||||||
|
},
|
||||||
|
30 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
try keys.append(allocator, key);
|
||||||
|
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"6" = true;
|
||||||
|
},
|
||||||
|
31 => {
|
||||||
|
key.ctrl = true;
|
||||||
|
key.@"7" = true;
|
||||||
|
try keys.append(allocator, key);
|
||||||
|
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"/" = true;
|
||||||
|
try keys.append(allocator, key);
|
||||||
|
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key._ = true;
|
||||||
|
},
|
||||||
|
// Standard ASCII characters
|
||||||
|
32 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@" " = true;
|
||||||
|
},
|
||||||
|
33 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"!" = true;
|
||||||
|
},
|
||||||
|
34 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"\"" = true;
|
||||||
|
},
|
||||||
|
35 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"#" = true;
|
||||||
|
},
|
||||||
|
36 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"$" = true;
|
||||||
|
},
|
||||||
|
37 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"%" = true;
|
||||||
|
},
|
||||||
|
38 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"&" = true;
|
||||||
|
},
|
||||||
|
39 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"'" = true;
|
||||||
|
},
|
||||||
|
40 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"(" = true;
|
||||||
|
},
|
||||||
|
41 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@")" = true;
|
||||||
|
},
|
||||||
|
42 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"*" = true;
|
||||||
|
},
|
||||||
|
43 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"+" = true;
|
||||||
|
},
|
||||||
|
44 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"," = true;
|
||||||
|
},
|
||||||
|
45 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"-" = true;
|
||||||
|
},
|
||||||
|
46 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"." = true;
|
||||||
|
},
|
||||||
|
47 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"/" = true;
|
||||||
|
},
|
||||||
|
48 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"0" = true;
|
||||||
|
},
|
||||||
|
49 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"1" = true;
|
||||||
|
},
|
||||||
|
50 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"2" = true;
|
||||||
|
},
|
||||||
|
51 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"3" = true;
|
||||||
|
},
|
||||||
|
52 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"4" = true;
|
||||||
|
},
|
||||||
|
53 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"5" = true;
|
||||||
|
},
|
||||||
|
54 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"6" = true;
|
||||||
|
},
|
||||||
|
55 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"7" = true;
|
||||||
|
},
|
||||||
|
56 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"8" = true;
|
||||||
|
},
|
||||||
|
57 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"9" = true;
|
||||||
|
},
|
||||||
|
58 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@":" = true;
|
||||||
|
},
|
||||||
|
59 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@";" = true;
|
||||||
|
},
|
||||||
|
60 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"<" = true;
|
||||||
|
},
|
||||||
|
61 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"=" = true;
|
||||||
|
},
|
||||||
|
62 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@">" = true;
|
||||||
|
},
|
||||||
|
63 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"?" = true;
|
||||||
|
},
|
||||||
|
64 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"@" = true;
|
||||||
|
},
|
||||||
|
65 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.a = true;
|
||||||
|
},
|
||||||
|
66 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.b = true;
|
||||||
|
},
|
||||||
|
67 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.c = true;
|
||||||
|
},
|
||||||
|
68 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.d = true;
|
||||||
|
},
|
||||||
|
69 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.e = true;
|
||||||
|
},
|
||||||
|
70 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.f = true;
|
||||||
|
},
|
||||||
|
71 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.g = true;
|
||||||
|
},
|
||||||
|
72 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.h = true;
|
||||||
|
},
|
||||||
|
73 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.i = true;
|
||||||
|
},
|
||||||
|
74 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.j = true;
|
||||||
|
},
|
||||||
|
75 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.k = true;
|
||||||
|
},
|
||||||
|
76 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.l = true;
|
||||||
|
},
|
||||||
|
77 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.m = true;
|
||||||
|
},
|
||||||
|
78 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.n = true;
|
||||||
|
},
|
||||||
|
79 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.o = true;
|
||||||
|
},
|
||||||
|
80 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.p = true;
|
||||||
|
},
|
||||||
|
81 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.q = true;
|
||||||
|
},
|
||||||
|
82 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.r = true;
|
||||||
|
},
|
||||||
|
83 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.s = true;
|
||||||
|
},
|
||||||
|
84 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.t = true;
|
||||||
|
},
|
||||||
|
85 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.u = true;
|
||||||
|
},
|
||||||
|
86 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.v = true;
|
||||||
|
},
|
||||||
|
87 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.w = true;
|
||||||
|
},
|
||||||
|
88 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.x = true;
|
||||||
|
},
|
||||||
|
89 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.y = true;
|
||||||
|
},
|
||||||
|
90 => {
|
||||||
|
key.shift = true;
|
||||||
|
key.z = true;
|
||||||
|
},
|
||||||
|
91 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"[" = true;
|
||||||
|
},
|
||||||
|
92 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"\\" = true;
|
||||||
|
},
|
||||||
|
93 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"]" = true;
|
||||||
|
},
|
||||||
|
94 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"^" = true;
|
||||||
|
},
|
||||||
|
95 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key._ = true;
|
||||||
|
},
|
||||||
|
96 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"`" = true;
|
||||||
|
},
|
||||||
|
97 => {
|
||||||
|
key.a = true;
|
||||||
|
},
|
||||||
|
98 => {
|
||||||
|
key.b = true;
|
||||||
|
},
|
||||||
|
99 => {
|
||||||
|
key.c = true;
|
||||||
|
},
|
||||||
|
100 => {
|
||||||
|
key.d = true;
|
||||||
|
},
|
||||||
|
101 => {
|
||||||
|
key.e = true;
|
||||||
|
},
|
||||||
|
102 => {
|
||||||
|
key.f = true;
|
||||||
|
},
|
||||||
|
103 => {
|
||||||
|
key.g = true;
|
||||||
|
},
|
||||||
|
104 => {
|
||||||
|
key.h = true;
|
||||||
|
},
|
||||||
|
105 => {
|
||||||
|
key.i = true;
|
||||||
|
},
|
||||||
|
106 => {
|
||||||
|
key.j = true;
|
||||||
|
},
|
||||||
|
107 => {
|
||||||
|
key.k = true;
|
||||||
|
},
|
||||||
|
108 => {
|
||||||
|
key.l = true;
|
||||||
|
},
|
||||||
|
109 => {
|
||||||
|
key.m = true;
|
||||||
|
},
|
||||||
|
110 => {
|
||||||
|
key.n = true;
|
||||||
|
},
|
||||||
|
111 => {
|
||||||
|
key.o = true;
|
||||||
|
},
|
||||||
|
112 => {
|
||||||
|
key.p = true;
|
||||||
|
},
|
||||||
|
113 => {
|
||||||
|
key.q = true;
|
||||||
|
},
|
||||||
|
114 => {
|
||||||
|
key.r = true;
|
||||||
|
},
|
||||||
|
115 => {
|
||||||
|
key.s = true;
|
||||||
|
},
|
||||||
|
116 => {
|
||||||
|
key.t = true;
|
||||||
|
},
|
||||||
|
117 => {
|
||||||
|
key.u = true;
|
||||||
|
},
|
||||||
|
118 => {
|
||||||
|
key.v = true;
|
||||||
|
},
|
||||||
|
119 => {
|
||||||
|
key.w = true;
|
||||||
|
},
|
||||||
|
120 => {
|
||||||
|
key.x = true;
|
||||||
|
},
|
||||||
|
121 => {
|
||||||
|
key.y = true;
|
||||||
|
},
|
||||||
|
122 => {
|
||||||
|
key.z = true;
|
||||||
|
},
|
||||||
|
123 => {
|
||||||
|
key.@"{" = true;
|
||||||
|
},
|
||||||
|
124 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"|" = true;
|
||||||
|
},
|
||||||
|
125 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"}" = true;
|
||||||
|
},
|
||||||
|
126 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.@"~" = true;
|
||||||
|
},
|
||||||
|
127 => {
|
||||||
|
key = std.mem.zeroes(Key);
|
||||||
|
key.backspace = true;
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try keys.append(allocator, key);
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
pub const ly_core = @import("ly-core");
|
||||||
|
|
||||||
|
pub const Cell = @import("Cell.zig");
|
||||||
|
pub const keyboard = @import("keyboard.zig");
|
||||||
|
pub const Position = @import("Position.zig");
|
||||||
|
pub const TerminalBuffer = @import("TerminalBuffer.zig");
|
||||||
|
pub const Widget = @import("Widget.zig");
|
||||||
|
|
||||||
|
pub const BigLabel = @import("components/BigLabel.zig");
|
||||||
|
pub const Box = @import("components/Box.zig");
|
||||||
|
pub const CyclableLabel = @import("components/generic.zig").CyclableLabel;
|
||||||
|
pub const Label = @import("components/Label.zig");
|
||||||
|
pub const Text = @import("components/Text.zig");
|
||||||
165
readme.md
165
readme.md
|
|
@ -2,25 +2,33 @@
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD,
|
_Note: the above animation can be found [here](https://codeberg.org/attachments/f336d6ac-8331-4323-91fc-0e4619803401)!_
|
||||||
designed with portability in mind (e.g. it does not require systemd to run).
|
|
||||||
|
|
||||||
Join us on Matrix over at [#ly:envs.net](https://matrix.to/#/#ly:envs.net)!
|
Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD, designed with portability in mind and doesn't require systemd to run.
|
||||||
|
|
||||||
**Note**: Development happens on [Codeberg](https://codeberg.org/fairyglade/ly)
|
Join us on Matrix over at [#ly-dm:matrix.org](https://matrix.to/#/#ly-dm:matrix.org)!
|
||||||
with a mirror on [GitHub](https://github.com/fairyglade/ly).
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Development happens on [Codeberg](https://codeberg.org/fairyglade/ly) with a mirror on [GitHub](https://github.com/fairyglade/ly).
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
- Compile-time:
|
- Compile-time:
|
||||||
- zig 0.15.x
|
- zig 0.16.x (you must use a __release version__ of zig; check that `zig version` does not have a `-dev*` suffix)
|
||||||
|
|
||||||
- libc
|
- libc
|
||||||
|
|
||||||
- pam
|
- pam
|
||||||
|
|
||||||
- xcb (optional, required by default; needed for X11 support)
|
- xcb (optional, required by default; needed for X11 support)
|
||||||
|
|
||||||
- Runtime (with default config):
|
- Runtime (with default config):
|
||||||
- xorg
|
- xorg
|
||||||
|
|
||||||
- xorg-xauth
|
- xorg-xauth
|
||||||
|
|
||||||
- shutdown
|
- shutdown
|
||||||
|
|
||||||
- brightnessctl
|
- brightnessctl
|
||||||
|
|
||||||
### Debian
|
### Debian
|
||||||
|
|
@ -31,8 +39,8 @@ with a mirror on [GitHub](https://github.com/fairyglade/ly).
|
||||||
|
|
||||||
### Fedora
|
### Fedora
|
||||||
|
|
||||||
**Warning**: You may encounter issues with SELinux on Fedora.
|
> [!WARNING]
|
||||||
It is recommended to add a rule for Ly as it currently does not ship one.
|
> You may encounter issues with SELinux on Fedora. It is recommended to add a rule for Ly as it currently does not ship one.
|
||||||
|
|
||||||
```
|
```
|
||||||
# dnf install kernel-devel pam-devel libxcb-devel zig xorg-x11-xauth xorg-x11-server brightnessctl
|
# dnf install kernel-devel pam-devel libxcb-devel zig xorg-x11-xauth xorg-x11-server brightnessctl
|
||||||
|
|
@ -50,16 +58,22 @@ It is recommended to add a rule for Ly as it currently does not ship one.
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
Ly has been tested with a wide variety of desktop environments and window
|
Every environment that works on other login managers also should work on Ly.
|
||||||
managers, all of which you can find in the sections below:
|
|
||||||
|
|
||||||
[Wayland environments](#supported-wayland-environments)
|
- Unlike most login managers Ly has an xinitrc and shell entry.
|
||||||
|
|
||||||
[X11 environments](#supported-x11-environments)
|
- If you installed your favorite environment and you don't see it, that's because Ly doesn't automatically refresh itself. To fix this you should restart Ly service (depends on your init system) or the easy way is to reboot your system.
|
||||||
|
|
||||||
|
- If your environment is still missing then check at `/usr/share/xsessions` or `/usr/share/wayland-sessions` to see if a .desktop file is present.
|
||||||
|
|
||||||
|
- If there isn't a .desktop file then create a new one at `/etc/ly/custom-sessions` that launches your favorite environment. These .desktop files can be only seen by Ly and if you want them system-wide you also can create at those directories instead.
|
||||||
|
|
||||||
|
- If Xorg sessions don't work then check if your distro compiles Ly with Xorg.
|
||||||
|
|
||||||
Logs are defined by `/etc/ly/config.ini`:
|
Logs are defined by `/etc/ly/config.ini`:
|
||||||
|
|
||||||
- The session log is located at `~/.local/state/ly-session.log` by default.
|
- The session log is located at `~/.local/state/ly-session.log` by default.
|
||||||
|
|
||||||
- The system log is located at `/var/log/ly.log` by default.
|
- The system log is located at `/var/log/ly.log` by default.
|
||||||
|
|
||||||
## Manually building
|
## Manually building
|
||||||
|
|
@ -72,23 +86,22 @@ $ cd ly
|
||||||
$ zig build
|
$ zig build
|
||||||
```
|
```
|
||||||
|
|
||||||
After building, you can (optionally) test Ly in a terminal emulator, although
|
After building, you can (optionally) test Ly in a terminal emulator, although authentication will **not** work:
|
||||||
authentication will **not** work:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$ zig build run
|
$ zig build run
|
||||||
```
|
```
|
||||||
|
|
||||||
**Important**: While you can also run Ly in a terminal emulator as root, it is
|
> [!IMPORTANT]
|
||||||
**not** recommended either. If you want to properly test Ly, please enable its
|
> While you can run Ly in a terminal emulator as root, it is **not** recommended. If you want to test Ly, please enable its service (as described below) and reboot your machine.
|
||||||
service (as described below) and reboot your machine.
|
|
||||||
|
|
||||||
The following sections show how to install Ly for a particular init system.
|
> [!NOTE]
|
||||||
Because the procedure is very similar for all of them, the commands will only
|
> You can, however, test your configuration file changes like that. Note that you must do Ctrl+C in order to exit Ly.
|
||||||
be detailed for the first section (which is about systemd).
|
|
||||||
|
|
||||||
**Note**: All following sections will assume you are using LightDM for
|
The next sections will explain how to use Ly with a variety of init systems. Detailed explanation is only given for systemd, but should be applicable for all.
|
||||||
convenience sake.
|
|
||||||
|
> [!NOTE]
|
||||||
|
> All following sections will assume you are using LightDM for convenience sake.
|
||||||
|
|
||||||
### systemd
|
### systemd
|
||||||
|
|
||||||
|
|
@ -98,11 +111,10 @@ Now, you can install Ly on your system:
|
||||||
# zig build installexe -Dinit_system=systemd
|
# zig build installexe -Dinit_system=systemd
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note**: The `init_system` parameter is optional and defaults to `systemd`.
|
> [!NOTE]
|
||||||
|
> The `init_system` parameter is optional and defaults to `systemd`.
|
||||||
|
|
||||||
Note that you also need to disable your current display manager. For example,
|
Note that you also need to disable your current display manager. For example, if you are using LightDM, you can execute the following command:
|
||||||
if LightDM is the current display manager, you can execute the following
|
|
||||||
command:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
# systemctl disable lightdm.service
|
# systemctl disable lightdm.service
|
||||||
|
|
@ -114,16 +126,20 @@ Then, similarly to the previous command, you need to enable the Ly service:
|
||||||
# systemctl enable ly@tty2.service
|
# systemctl enable ly@tty2.service
|
||||||
```
|
```
|
||||||
|
|
||||||
**Important**: Because Ly runs in a TTY, you **must** disable the TTY service
|
> [!IMPORTANT]
|
||||||
that Ly will run on, otherwise bad things will happen. For example, to disable `getty` spawning on TTY 2, you need to execute the following command:
|
> Because Ly runs in a TTY, you **must** disable the TTY service that Ly will run on, otherwise bad things will happen. For example, to disable `getty` spawning on TTY 2, you need to execute the following command:
|
||||||
|
|
||||||
```
|
```
|
||||||
# systemctl disable getty@tty2.service
|
# systemctl disable getty@tty2.service
|
||||||
```
|
```
|
||||||
|
|
||||||
You can change the TTY Ly will run on by editing the corresponding
|
On platforms that use systemd-logind to dynamically start `autovt@.service` instances when the switch to a new tty occurs, any ly instances for ttys _except the default tty_ need to be enabled using a different mechanism: To autostart ly on switch to `tty2`, do not enable any `ly` unit directly, instead symlink `autovt@tty2.service` to `ly@tty2.service` within `/usr/lib/systemd/system/` (analogous for every other tty you want to enable ly on).
|
||||||
service file for your platform, or on systemd, by enabling the service on
|
|
||||||
different TTYs, as is done above.
|
The target of the symlink, `ly@ttyN.service`, does not actually exist, but systemd nevertheless recognizes that the instanciation of `autovt@.service` with `%I` equal to `ttyN` now points to an instanciation of `ly@.service` with `%I` set to `ttyN`.
|
||||||
|
|
||||||
|
Compare to `man 5 logind.conf`, especially regarding the `NAutoVTs=` and `ReserveVT=` parameters.
|
||||||
|
|
||||||
|
On non-systemd systems, you can change the TTY Ly will run on by editing the corresponding service file for your platform.
|
||||||
|
|
||||||
### OpenRC
|
### OpenRC
|
||||||
|
|
||||||
|
|
@ -134,8 +150,8 @@ different TTYs, as is done above.
|
||||||
# rc-update del agetty.tty2
|
# rc-update del agetty.tty2
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note**: On Gentoo specifically, you also **must** comment out the appropriate
|
> [!NOTE]
|
||||||
line for the TTY in /etc/inittab.
|
> On Gentoo specifically, you also **must** comment out the appropriate line for the TTY in /etc/inittab.
|
||||||
|
|
||||||
### runit
|
### runit
|
||||||
|
|
||||||
|
|
@ -166,8 +182,7 @@ To disable TTY 2, edit `/etc/s6/config/tty2.conf` and set `SPAWN="no"`.
|
||||||
# dinitctl enable ly
|
# dinitctl enable ly
|
||||||
```
|
```
|
||||||
|
|
||||||
To disable TTY 2, go to `/etc/dinit.d/config/console.conf` and modify
|
To disable TTY 2, go to `/etc/dinit.d/config/console.conf` and modify `ACTIVE_CONSOLES`.
|
||||||
`ACTIVE_CONSOLES`.
|
|
||||||
|
|
||||||
### sysvinit
|
### sysvinit
|
||||||
|
|
||||||
|
|
@ -194,8 +209,7 @@ Ly:\
|
||||||
:al=root:
|
:al=root:
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, modify the command field of the `ttyv1` terminal entry in `/etc/ttys`
|
Then, modify the command field of the `ttyv1` terminal entry in `/etc/ttys` (TTYs in FreeBSD start at 0):
|
||||||
(TTYs in FreeBSD start at 0):
|
|
||||||
|
|
||||||
```
|
```
|
||||||
ttyv1 "/usr/libexec/getty Ly" xterm on secure
|
ttyv1 "/usr/libexec/getty Ly" xterm on secure
|
||||||
|
|
@ -203,37 +217,33 @@ ttyv1 "/usr/libexec/getty Ly" xterm on secure
|
||||||
|
|
||||||
### Updating
|
### Updating
|
||||||
|
|
||||||
You can also install Ly without overrding the current configuration file. This
|
You can also install Ly without overrding the current configuration file. This is called **updating**. To update, simply run:
|
||||||
is called **updating**. To update, simply run:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
# zig build installnoconf
|
# zig build installnoconf
|
||||||
```
|
```
|
||||||
|
|
||||||
You can, of course, still select the init system of your choice when using this
|
You can, of course, still select the init system of your choice when using this command.
|
||||||
command.
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
You can find all the configuration in `/etc/ly/config.ini`. The file is fully
|
You can find all the configuration in `/etc/ly/config.ini`. The file is fully commented, and includes the default values.
|
||||||
commented, and includes the default values.
|
|
||||||
|
You may also check the validity of your configuration file (i.e. if there are any errors in it) with the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ly --validate-config /etc/ly/config.ini
|
||||||
|
```
|
||||||
|
|
||||||
## Controls
|
## Controls
|
||||||
|
|
||||||
Use the Up/Down arrow keys to change the current field, and the Left/Right
|
Use the Up/Down arrow keys to change the current field, and the Left/Right arrow keys to scroll through the different fields (whether it be the info line, the desktop environment, or the username). The info line is where messages and errors are displayed.
|
||||||
arrow keys to scroll through the different fields (whether it be the info line,
|
|
||||||
the desktop environment, or the username). The info line is where messages and
|
|
||||||
errors are displayed.
|
|
||||||
|
|
||||||
## A note on .xinitrc
|
## A note on .xinitrc
|
||||||
|
|
||||||
If your `.xinitrc` file doesn't work ,make sure it is executable and includes a
|
If your `.xinitrc` file doesn't work ,make sure it is executable and includes a shebang. This file is supposed to be a shell script! Quoting from `xinit`'s man page:
|
||||||
shebang. This file is supposed to be a shell script! Quoting from `xinit`'s man
|
|
||||||
page:
|
|
||||||
|
|
||||||
> If no specific client program is given on the command line, xinit will look
|
> If no specific client program is given on the command line, xinit will look for a file in the user's home directory called .xinitrc to run as a shell script to start up client programs.
|
||||||
> for a file in the user's home directory called .xinitrc to run as a shell
|
|
||||||
> script to start up client programs.
|
|
||||||
|
|
||||||
A typical shebang for a shell script looks like this:
|
A typical shebang for a shell script looks like this:
|
||||||
|
|
||||||
|
|
@ -244,47 +254,18 @@ A typical shebang for a shell script looks like this:
|
||||||
## Tips
|
## Tips
|
||||||
|
|
||||||
- The numlock and capslock state is printed in the top-right corner.
|
- The numlock and capslock state is printed in the top-right corner.
|
||||||
|
|
||||||
- Use the F1 and F2 keys to respectively shutdown and reboot.
|
- Use the F1 and F2 keys to respectively shutdown and reboot.
|
||||||
- Take a look at your `.xsession` file if X doesn't start, as it can interfere
|
|
||||||
(this file is launched with X to configure the display properly).
|
|
||||||
|
|
||||||
## Supported Wayland environments
|
- Take a look at your `.xsession` file if X doesn't start, as it can interfere (this file is launched with X to configure the display properly).
|
||||||
|
|
||||||
- budgie
|
|
||||||
- cosmic
|
|
||||||
- deepin
|
|
||||||
- enlightenment
|
|
||||||
- gnome
|
|
||||||
- hyprland
|
|
||||||
- kde
|
|
||||||
- labwc
|
|
||||||
- niri
|
|
||||||
- pantheon
|
|
||||||
- sway
|
|
||||||
- weston
|
|
||||||
|
|
||||||
## Supported X11 environments
|
|
||||||
|
|
||||||
- awesome
|
|
||||||
- bspwm
|
|
||||||
- budgie
|
|
||||||
- cinnamon
|
|
||||||
- dwm
|
|
||||||
- enlightenment
|
|
||||||
- gnome
|
|
||||||
- kde
|
|
||||||
- leftwm
|
|
||||||
- lxde
|
|
||||||
- mate
|
|
||||||
- maxx
|
|
||||||
- pantheon
|
|
||||||
- qwm
|
|
||||||
- spectrwm
|
|
||||||
- windowmaker
|
|
||||||
- xfce
|
|
||||||
- xmonad
|
|
||||||
|
|
||||||
## A final note
|
## A final note
|
||||||
|
|
||||||
The name "Ly" is a tribute to the fairy from the game Rayman. Ly was tested by
|
The name "Ly" is a tribute to the fairy from the game Rayman. Ly was tested by oxodao, who is some seriously awesome dude.
|
||||||
oxodao, who is some seriously awesome dude.
|
|
||||||
|
Also, Ly wouldn't be there today without [ashametrine](https://github.com/ashametrine), who has done significant contributions to the project for the Zig rewrite, which lead to the release of Ly v1.0.0. Massive thanks, and sorry for not crediting you enough beforehand!
|
||||||
|
|
||||||
|
### Donate
|
||||||
|
|
||||||
|
If you like Ly and wish to support my work further, feel free to donate via my
|
||||||
|
[Liberapay link](https://liberapay.com/ShiningLea)!
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@ allow_empty_password = true
|
||||||
# dur_file -> .dur file format (https://github.com/cmang/durdraw/tree/master)
|
# dur_file -> .dur file format (https://github.com/cmang/durdraw/tree/master)
|
||||||
animation = none
|
animation = none
|
||||||
|
|
||||||
|
# Delay between each animation frame in milliseconds
|
||||||
|
animation_frame_delay = 5
|
||||||
|
|
||||||
# Stop the animation after some time
|
# Stop the animation after some time
|
||||||
# 0 -> Run forever
|
# 0 -> Run forever
|
||||||
# 1..2e12 -> Stop the animation after this many seconds
|
# 1..2e12 -> Stop the animation after this many seconds
|
||||||
|
|
@ -62,7 +65,7 @@ auto_login_service = ly-autologin
|
||||||
# To find available session names, check the .desktop files in:
|
# To find available session names, check the .desktop files in:
|
||||||
# - /usr/share/xsessions/ (for X11 sessions)
|
# - /usr/share/xsessions/ (for X11 sessions)
|
||||||
# - /usr/share/wayland-sessions/ (for Wayland sessions)
|
# - /usr/share/wayland-sessions/ (for Wayland sessions)
|
||||||
# Use the filename without .desktop extension, or the value of DesktopNames field
|
# Use the filename without .desktop extension, the Name field inside the file or the value of the DesktopNames field
|
||||||
# Examples: "i3", "sway", "gnome", "plasma", "xfce"
|
# Examples: "i3", "sway", "gnome", "plasma", "xfce"
|
||||||
# If null, automatic login is disabled
|
# If null, automatic login is disabled
|
||||||
auto_login_session = null
|
auto_login_session = null
|
||||||
|
|
@ -94,6 +97,14 @@ blank_box = true
|
||||||
# Border foreground color id
|
# Border foreground color id
|
||||||
border_fg = 0x00FFFFFF
|
border_fg = 0x00FFFFFF
|
||||||
|
|
||||||
|
# Relative horizontal position from the end of the screen
|
||||||
|
# default: 0.5
|
||||||
|
box_position_h = 0.5
|
||||||
|
|
||||||
|
# Relative vertical position from the bottom of the screen
|
||||||
|
# default: 0.4
|
||||||
|
box_position_v = 0.4
|
||||||
|
|
||||||
# Title to show at the top of the main box
|
# Title to show at the top of the main box
|
||||||
# If set to null, none will be shown
|
# If set to null, none will be shown
|
||||||
box_title = null
|
box_title = null
|
||||||
|
|
@ -101,13 +112,13 @@ box_title = null
|
||||||
# Brightness decrease command
|
# Brightness decrease command
|
||||||
brightness_down_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q -n s 10%-
|
brightness_down_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q -n s 10%-
|
||||||
|
|
||||||
# Brightness decrease key, or null to disable
|
# Brightness decrease key combination, or null to disable
|
||||||
brightness_down_key = F5
|
brightness_down_key = F5
|
||||||
|
|
||||||
# Brightness increase command
|
# Brightness increase command
|
||||||
brightness_up_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q -n s +10%
|
brightness_up_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q -n s +10%
|
||||||
|
|
||||||
# Brightness increase key, or null to disable
|
# Brightness increase key combination, or null to disable
|
||||||
brightness_up_key = F6
|
brightness_up_key = F6
|
||||||
|
|
||||||
# Erase password input on failure
|
# Erase password input on failure
|
||||||
|
|
@ -140,6 +151,11 @@ colormix_col2 = 0x000000FF
|
||||||
# Color mixing animation third color id
|
# Color mixing animation third color id
|
||||||
colormix_col3 = 0x20000000
|
colormix_col3 = 0x20000000
|
||||||
|
|
||||||
|
# For custom binds: the horizontal limit in characters for each
|
||||||
|
# line of custom binds before moving on to the next.
|
||||||
|
# If null, defaults to the width of the terminal instead.
|
||||||
|
custom_bind_width = null
|
||||||
|
|
||||||
# Custom sessions directory
|
# Custom sessions directory
|
||||||
# You can specify multiple directories,
|
# You can specify multiple directories,
|
||||||
# e.g. $CONFIG_DIRECTORY/ly/custom-sessions:$PREFIX_DIRECTORY/share/custom-sessions
|
# e.g. $CONFIG_DIRECTORY/ly/custom-sessions:$PREFIX_DIRECTORY/share/custom-sessions
|
||||||
|
|
@ -167,10 +183,15 @@ doom_bottom_color = 0x00FFFFFF
|
||||||
# Dur file path
|
# Dur file path
|
||||||
dur_file_path = $CONFIG_DIRECTORY/ly/example.dur
|
dur_file_path = $CONFIG_DIRECTORY/ly/example.dur
|
||||||
|
|
||||||
# Dur offset x direction
|
# Dur file alignment
|
||||||
|
# The dur file can be aligned with a direction and centered easily with the flags below
|
||||||
|
# Available inputs: topleft, topcenter, topright, centerleft, center, centerright, bottomleft, bottomcenter, bottomright
|
||||||
|
dur_offset_alignment = center
|
||||||
|
|
||||||
|
# Dur offset x direction (value is added to the current position determined by alignment, negatives are supported)
|
||||||
dur_x_offset = 0
|
dur_x_offset = 0
|
||||||
|
|
||||||
# Dur offset y direction
|
# Dur offset y direction (value is added to the current position determined by alignment, negatives are supported)
|
||||||
dur_y_offset = 0
|
dur_y_offset = 0
|
||||||
|
|
||||||
# Set margin to the edges of the DM (useful for curved monitors)
|
# Set margin to the edges of the DM (useful for curved monitors)
|
||||||
|
|
@ -228,7 +249,7 @@ gameoflife_initial_density = 0.4
|
||||||
# Command executed when pressing hibernate key (can be null)
|
# Command executed when pressing hibernate key (can be null)
|
||||||
hibernate_cmd = null
|
hibernate_cmd = null
|
||||||
|
|
||||||
# Specifies the key used for hibernate (F1-F12)
|
# Specifies the key combination used for hibernate
|
||||||
hibernate_key = F4
|
hibernate_key = F4
|
||||||
|
|
||||||
# Remove main box borders
|
# Remove main box borders
|
||||||
|
|
@ -278,6 +299,7 @@ login_defs_path = /etc/login.defs
|
||||||
logout_cmd = null
|
logout_cmd = null
|
||||||
|
|
||||||
# General log file path
|
# General log file path
|
||||||
|
# If null, syslog will be used instead
|
||||||
ly_log = /var/log/ly.log
|
ly_log = /var/log/ly.log
|
||||||
|
|
||||||
# Main box horizontal margin
|
# Main box horizontal margin
|
||||||
|
|
@ -286,9 +308,6 @@ margin_box_h = 2
|
||||||
# Main box vertical margin
|
# Main box vertical margin
|
||||||
margin_box_v = 1
|
margin_box_v = 1
|
||||||
|
|
||||||
# Event timeout in milliseconds
|
|
||||||
min_refresh_delta = 5
|
|
||||||
|
|
||||||
# Set numlock on/off at startup
|
# Set numlock on/off at startup
|
||||||
numlock = false
|
numlock = false
|
||||||
|
|
||||||
|
|
@ -299,7 +318,7 @@ path = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||||
# Command executed when pressing restart_key
|
# Command executed when pressing restart_key
|
||||||
restart_cmd = /sbin/shutdown -r now
|
restart_cmd = /sbin/shutdown -r now
|
||||||
|
|
||||||
# Specifies the key used for restart (F1-F12)
|
# Specifies the key combination used for restart
|
||||||
restart_key = F2
|
restart_key = F2
|
||||||
|
|
||||||
# Save the current desktop and login as defaults, and load them on startup
|
# Save the current desktop and login as defaults, and load them on startup
|
||||||
|
|
@ -311,29 +330,42 @@ service_name = ly
|
||||||
# Session log file path
|
# Session log file path
|
||||||
# This will contain stdout and stderr of Wayland sessions
|
# This will contain stdout and stderr of Wayland sessions
|
||||||
# By default it's saved in the user's home directory
|
# By default it's saved in the user's home directory
|
||||||
# Important: due to technical limitations, X11 and shell sessions aren't supported, which
|
# Important: due to technical limitations, X11, shell sessions as well as
|
||||||
# means you won't get any logs from those sessions.
|
# launching session via KMSCON aren't supported, which means you won't get any
|
||||||
|
# logs from those sessions.
|
||||||
# If null, no session log will be created
|
# If null, no session log will be created
|
||||||
session_log = .local/state/ly-session.log
|
session_log = .local/state/ly-session.log
|
||||||
|
|
||||||
# Setup command
|
# Setup command
|
||||||
setup_cmd = $CONFIG_DIRECTORY/ly/setup.sh
|
setup_cmd = $CONFIG_DIRECTORY/ly/setup.sh
|
||||||
|
|
||||||
|
# Show the shell session in the session list
|
||||||
|
# If false, the shell session will be hidden
|
||||||
|
shell = true
|
||||||
|
|
||||||
|
# Specifies the key combination used for showing the password
|
||||||
|
show_password_key = F7
|
||||||
|
|
||||||
|
# Display the active TTY number (e.g. tty3) to the right of the clock in the top right corner
|
||||||
|
# If the clock is disabled, the TTY label occupies the top right corner on its own
|
||||||
|
# If false, the TTY number will not be shown
|
||||||
|
show_tty = false
|
||||||
|
|
||||||
# Command executed when pressing shutdown_key
|
# Command executed when pressing shutdown_key
|
||||||
shutdown_cmd = /sbin/shutdown $PLATFORM_SHUTDOWN_ARG now
|
shutdown_cmd = /sbin/shutdown $PLATFORM_SHUTDOWN_ARG now
|
||||||
|
|
||||||
# Specifies the key used for shutdown (F1-F12)
|
# Specifies the key combination used for shutdown
|
||||||
shutdown_key = F1
|
shutdown_key = F1
|
||||||
|
|
||||||
# Command executed when pressing sleep key (can be null)
|
# Command executed when pressing sleep key (can be null)
|
||||||
sleep_cmd = null
|
sleep_cmd = null
|
||||||
|
|
||||||
# Specifies the key used for sleep (F1-F12)
|
# Specifies the key combination used for sleep
|
||||||
sleep_key = F3
|
sleep_key = F3
|
||||||
|
|
||||||
# Command executed when starting Ly (before the TTY is taken control of)
|
# Command executed when starting Ly (before the TTY is taken control of)
|
||||||
# If null, no command will be executed
|
# See file at path below for an example of changing the default TTY colors
|
||||||
start_cmd = null
|
start_cmd = $CONFIG_DIRECTORY/ly/startup.sh
|
||||||
|
|
||||||
# Center the session name.
|
# Center the session name.
|
||||||
text_in_center = false
|
text_in_center = false
|
||||||
|
|
@ -349,11 +381,18 @@ vi_mode = false
|
||||||
# Wayland desktop environments
|
# Wayland desktop environments
|
||||||
# You can specify multiple directories,
|
# You can specify multiple directories,
|
||||||
# e.g. $PREFIX_DIRECTORY/share/wayland-sessions:$PREFIX_DIRECTORY/local/share/wayland-sessions
|
# e.g. $PREFIX_DIRECTORY/share/wayland-sessions:$PREFIX_DIRECTORY/local/share/wayland-sessions
|
||||||
|
# If null, Wayland sessions will not be shown
|
||||||
waylandsessions = $PREFIX_DIRECTORY/share/wayland-sessions
|
waylandsessions = $PREFIX_DIRECTORY/share/wayland-sessions
|
||||||
|
|
||||||
# Xorg server command
|
# Xorg server command
|
||||||
|
# Add the -quiet argument to hide startup logs from the server
|
||||||
x_cmd = $PREFIX_DIRECTORY/bin/X
|
x_cmd = $PREFIX_DIRECTORY/bin/X
|
||||||
|
|
||||||
|
# Xorg virtual terminal number
|
||||||
|
# Mostly useful for FreeBSD where choosing the current TTY causes issues
|
||||||
|
# If null, the current TTY will be chosen
|
||||||
|
x_vt = null
|
||||||
|
|
||||||
# Xorg xauthority edition tool
|
# Xorg xauthority edition tool
|
||||||
xauth_cmd = $PREFIX_DIRECTORY/bin/xauth
|
xauth_cmd = $PREFIX_DIRECTORY/bin/xauth
|
||||||
|
|
||||||
|
|
@ -364,4 +403,29 @@ xinitrc = ~/.xinitrc
|
||||||
# Xorg desktop environments
|
# Xorg desktop environments
|
||||||
# You can specify multiple directories,
|
# You can specify multiple directories,
|
||||||
# e.g. $PREFIX_DIRECTORY/share/xsessions:$PREFIX_DIRECTORY/local/share/xsessions
|
# e.g. $PREFIX_DIRECTORY/share/xsessions:$PREFIX_DIRECTORY/local/share/xsessions
|
||||||
|
# If null, X11 sessions will not be shown
|
||||||
xsessions = $PREFIX_DIRECTORY/share/xsessions
|
xsessions = $PREFIX_DIRECTORY/share/xsessions
|
||||||
|
|
||||||
|
# Custom Commands and Labels:
|
||||||
|
# The following examples below give an outline for setting up custom commands and labels.
|
||||||
|
# Unless specified as optional, an option is mandatory.
|
||||||
|
|
||||||
|
# Comments preceding with '##' are for documentation.
|
||||||
|
# Comments preceding with '#' comment out the example INI.
|
||||||
|
|
||||||
|
## Declare a command with the F8 binding.
|
||||||
|
#[cmd:F8]
|
||||||
|
## The name of the command to show up in Ly.
|
||||||
|
## Note: "$" in "$brightness_up" fetches the appropriate string from the specified locale file
|
||||||
|
## and is replaced with the value representing "brightness_up".
|
||||||
|
## You can see the list of keys in any locale file in $CONFIG_DIRECTORY/ly/lang.
|
||||||
|
#cmd = touch /tmp/ly.gaming
|
||||||
|
#name = custom command $brightness_up
|
||||||
|
|
||||||
|
## Declare a label with an ID. This ID should be unique across all labels.
|
||||||
|
#[lbl:kernel]
|
||||||
|
#cmd = uname -srn
|
||||||
|
## Optional, defaulting to 0.
|
||||||
|
## In frames, the time to re-run the command and update the label.
|
||||||
|
## If 0, only run once and do not refresh afterwards
|
||||||
|
#refresh = 0
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ redirected to the session log file found in Ly's configuration file. If set to
|
||||||
true, Ly will consider the program is going to run in a TTY, and thus will not
|
true, Ly will consider the program is going to run in a TTY, and thus will not
|
||||||
redirect standard output & error. It is optional and defaults to false.
|
redirect standard output & error. It is optional and defaults to false.
|
||||||
|
|
||||||
Finally, do note that, if the Terminal value is set to true, the
|
Finally, do note that if the Terminal value is set to true, the
|
||||||
XDG_SESSION_TYPE environment variable will be set to "tty". Otherwise, it will
|
XDG_SESSION_TYPE environment variable will be set to "tty". Otherwise, it will
|
||||||
be set to "unspecified" (without quotes), which is behavior that at least
|
be set to "unspecified" (without quotes), which is behavior that at least
|
||||||
systemd recognizes (see pam_systemd's man page).
|
systemd recognizes (see pam_systemd's man page).
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -2,26 +2,29 @@ authenticating = جاري المصادقة...
|
||||||
brightness_down = خفض السطوع
|
brightness_down = خفض السطوع
|
||||||
brightness_up = رفع السطوع
|
brightness_up = رفع السطوع
|
||||||
capslock = capslock
|
capslock = capslock
|
||||||
|
custom = مخصص
|
||||||
|
custom_info_err_output_long = الإخراج طويل جداً
|
||||||
|
custom_info_err_no_output = لا يوجد إخراج
|
||||||
|
custom_info_err_no_output_error = ، خطأ محتمل
|
||||||
err_alloc = فشل في تخصيص الذاكرة
|
err_alloc = فشل في تخصيص الذاكرة
|
||||||
|
err_args = تعذر تحليل وسيطات سطر الأوامر
|
||||||
|
err_autologin_session = لم يتم العثور على جلسة تسجيل الدخول التلقائي
|
||||||
err_bounds = out-of-bounds index
|
err_bounds = out-of-bounds index
|
||||||
err_brightness_change = فشل في تغيير سطوع الشاشة
|
err_brightness_change = فشل في تغيير سطوع الشاشة
|
||||||
err_chdir = فشل في فتح مجلد المنزل
|
err_chdir = فشل في فتح مجلد المنزل
|
||||||
|
err_clock_too_long = نص الساعة طويل جداً
|
||||||
err_config = فشل في تفسير ملف الإعدادات
|
err_config = فشل في تفسير ملف الإعدادات
|
||||||
|
err_crawl = فشل الزحف في أدلة الجلسة
|
||||||
err_dgn_oob = رسالة سجل (Log)
|
err_dgn_oob = رسالة سجل (Log)
|
||||||
err_domain = اسم نطاق غير صالح
|
err_domain = اسم نطاق غير صالح
|
||||||
err_empty_password = لا يُسمح بكلمة مرور فارغة
|
err_empty_password = لا يُسمح بكلمة مرور فارغة
|
||||||
err_envlist = فشل في جلب قائمة المتغيرات البيئية
|
err_envlist = فشل في جلب قائمة المتغيرات البيئية
|
||||||
|
err_get_active_tty = فشل الحصول على tty النشط
|
||||||
|
err_hibernate = فشل تنفيذ أمر الإسبات
|
||||||
err_hostname = فشل في جلب اسم المضيف (Hostname)
|
err_hostname = فشل في جلب اسم المضيف (Hostname)
|
||||||
|
err_inactivity = فشل تنفيذ أمر عدم النشاط
|
||||||
|
err_lock_state = فشل الحصول على حالة القفل
|
||||||
|
err_log = فشل فتح ملف السجل
|
||||||
err_mlock = فشل في تأمين ذاكرة كلمة المرور (mlock)
|
err_mlock = فشل في تأمين ذاكرة كلمة المرور (mlock)
|
||||||
err_null = مؤشر فارغ (Null pointer)
|
err_null = مؤشر فارغ (Null pointer)
|
||||||
err_numlock = فشل في ضبط Num Lock
|
err_numlock = فشل في ضبط Num Lock
|
||||||
|
|
@ -47,12 +50,12 @@ err_perm_group = فشل في تخفيض صلاحيات المجموعة (Group p
|
||||||
err_perm_user = فشل في تخفيض صلاحيات المستخدم (User permissions)
|
err_perm_user = فشل في تخفيض صلاحيات المستخدم (User permissions)
|
||||||
err_pwnam = فشل في جلب معلومات المستخدم
|
err_pwnam = فشل في جلب معلومات المستخدم
|
||||||
err_sleep = فشل في تنفيذ أمر sleep
|
err_sleep = فشل في تنفيذ أمر sleep
|
||||||
|
err_start = فشل تنفيذ أمر البدء
|
||||||
|
err_battery = فشل تحميل حالة البطارية
|
||||||
|
err_switch_tty = فشل تبديل tty
|
||||||
err_tty_ctrl = فشل في نقل تحكم الطرفية (TTY)
|
err_tty_ctrl = فشل في نقل تحكم الطرفية (TTY)
|
||||||
|
err_no_users = لم يتم العثور على مستخدمين
|
||||||
|
err_uid_range = فشل الحصول الديناميكي على نطاق uid
|
||||||
err_user_gid = فشل في تعيين معرّف المجموعة (GID) للمستخدم
|
err_user_gid = فشل في تعيين معرّف المجموعة (GID) للمستخدم
|
||||||
err_user_init = فشل في تهيئة بيانات المستخدم
|
err_user_init = فشل في تهيئة بيانات المستخدم
|
||||||
err_user_uid = فشل في تعيين معرّف المستخدم (UID)
|
err_user_uid = فشل في تعيين معرّف المستخدم (UID)
|
||||||
|
|
@ -60,11 +63,11 @@ err_xauth = فشل في تنفيذ أمر xauth
|
||||||
err_xcb_conn = فشل في الاتصال بمكتبة XCB
|
err_xcb_conn = فشل في الاتصال بمكتبة XCB
|
||||||
err_xsessions_dir = فشل في العثور على مجلد Xsessions
|
err_xsessions_dir = فشل في العثور على مجلد Xsessions
|
||||||
err_xsessions_open = فشل في فتح مجلد Xsessions
|
err_xsessions_open = فشل في فتح مجلد Xsessions
|
||||||
|
hibernate = إسبات
|
||||||
insert = ادخال
|
insert = ادخال
|
||||||
login = تسجيل الدخول
|
login = تسجيل الدخول
|
||||||
logout = تم تسجيل خروجك
|
logout = تم تسجيل خروجك
|
||||||
no_x11_support = تم تعطيل دعم x11 اثناء وقت الـ compile
|
no_x11_support = دعم x11 معطّل في وقت الترجمة
|
||||||
normal = عادي
|
normal = عادي
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
other = اخر
|
other = اخر
|
||||||
|
|
@ -73,6 +76,7 @@ restart = اعادة التشغيل
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = ايقاف التشغيل
|
shutdown = ايقاف التشغيل
|
||||||
sleep = وضع السكون
|
sleep = وضع السكون
|
||||||
|
toggle_password = إظهار/إخفاء كلمة المرور
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
x11 = x11
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ brightness_down = намаляване на яркостта
|
||||||
brightness_up = увеличаване на яркостта
|
brightness_up = увеличаване на яркостта
|
||||||
capslock = caps lock
|
capslock = caps lock
|
||||||
custom = персонализирано
|
custom = персонализирано
|
||||||
|
custom_info_err_output_long = резултатът е твърде дълъг
|
||||||
|
custom_info_err_no_output = няма резултат
|
||||||
|
custom_info_err_no_output_error = , възможна грешка
|
||||||
err_alloc = неуспешно заделяне на памет
|
err_alloc = неуспешно заделяне на памет
|
||||||
err_args = неуспешен анализ на аргументите от командния ред
|
err_args = неуспешен анализ на аргументите от командния ред
|
||||||
err_autologin_session = сесията за автоматично влизане не е намерена
|
err_autologin_session = сесията за автоматично влизане не е намерена
|
||||||
|
|
@ -73,6 +76,7 @@ restart = рестартиране
|
||||||
shell = обвивка
|
shell = обвивка
|
||||||
shutdown = изключване
|
shutdown = изключване
|
||||||
sleep = заспиване
|
sleep = заспиване
|
||||||
|
toggle_password = превключване на паролата
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
x11 = x11
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -2,26 +2,29 @@ authenticating = autenticant...
|
||||||
brightness_down = abaixar brillantor
|
brightness_down = abaixar brillantor
|
||||||
brightness_up = apujar brillantor
|
brightness_up = apujar brillantor
|
||||||
capslock = Bloq Majús
|
capslock = Bloq Majús
|
||||||
|
custom = personalitzat
|
||||||
|
custom_info_err_output_long = sortida massa llarga
|
||||||
|
custom_info_err_no_output = sense sortida
|
||||||
|
custom_info_err_no_output_error = , possible error
|
||||||
err_alloc = assignació de memòria fallida
|
err_alloc = assignació de memòria fallida
|
||||||
|
err_args = no s'han pogut analitzar els arguments de la línia d'ordres
|
||||||
|
err_autologin_session = no s'ha trobat la sessió d'inici de sessió automàtic
|
||||||
err_bounds = índex fora de límits
|
err_bounds = índex fora de límits
|
||||||
err_brightness_change = error en canviar la brillantor
|
err_brightness_change = error en canviar la brillantor
|
||||||
err_chdir = error en obrir la carpeta home
|
err_chdir = error en obrir la carpeta home
|
||||||
|
err_clock_too_long = la cadena del rellotge és massa llarga
|
||||||
|
err_config = no s'ha pogut analitzar el fitxer de configuració
|
||||||
|
err_crawl = no s'han pogut explorar els directoris de sessió
|
||||||
err_dgn_oob = missatge de registre
|
err_dgn_oob = missatge de registre
|
||||||
err_domain = domini invàlid
|
err_domain = domini invàlid
|
||||||
|
err_empty_password = no es permet la contrasenya buida
|
||||||
err_envlist = error en obtenir l'envlist
|
err_envlist = error en obtenir l'envlist
|
||||||
|
err_get_active_tty = no s'ha pogut obtenir el tty actiu
|
||||||
|
err_hibernate = no s'ha pogut executar l'ordre d'hibernació
|
||||||
err_hostname = error en obtenir el nom de l'amfitrió
|
err_hostname = error en obtenir el nom de l'amfitrió
|
||||||
|
err_inactivity = no s'ha pogut executar l'ordre d'inactivitat
|
||||||
|
err_lock_state = no s'ha pogut obtenir l'estat de bloqueig
|
||||||
|
err_log = no s'ha pogut obrir el fitxer de registre
|
||||||
err_mlock = error en bloquejar la memòria de clau
|
err_mlock = error en bloquejar la memòria de clau
|
||||||
err_null = punter nul
|
err_null = punter nul
|
||||||
err_numlock = error en establir el Bloq num
|
err_numlock = error en establir el Bloq num
|
||||||
|
|
@ -46,13 +49,13 @@ err_perm_dir = error en canviar el directori actual
|
||||||
err_perm_group = error en degradar els permisos de grup
|
err_perm_group = error en degradar els permisos de grup
|
||||||
err_perm_user = error en degradar els permisos de l'usuari
|
err_perm_user = error en degradar els permisos de l'usuari
|
||||||
err_pwnam = error en obtenir la informació de l'usuari
|
err_pwnam = error en obtenir la informació de l'usuari
|
||||||
|
err_sleep = no s'ha pogut executar l'ordre de suspensió
|
||||||
|
err_start = no s'ha pogut executar l'ordre d'inici
|
||||||
|
err_battery = no s'ha pogut carregar l'estat de la bateria
|
||||||
|
err_switch_tty = no s'ha pogut canviar de tty
|
||||||
|
err_tty_ctrl = ha fallat la transferència del control tty
|
||||||
|
err_no_users = no s'han trobat usuaris
|
||||||
|
err_uid_range = no s'ha pogut obtenir dinàmicament el rang d'uid
|
||||||
err_user_gid = error en establir el GID de l'usuari
|
err_user_gid = error en establir el GID de l'usuari
|
||||||
err_user_init = error en inicialitzar usuari
|
err_user_init = error en inicialitzar usuari
|
||||||
err_user_uid = error en establir l'UID de l'usuari
|
err_user_uid = error en establir l'UID de l'usuari
|
||||||
|
|
@ -60,19 +63,20 @@ err_xauth = error en la comanda xauth
|
||||||
err_xcb_conn = error en la connexió xcb
|
err_xcb_conn = error en la connexió xcb
|
||||||
err_xsessions_dir = error en trobar la carpeta de sessions
|
err_xsessions_dir = error en trobar la carpeta de sessions
|
||||||
err_xsessions_open = error en obrir la carpeta de sessions
|
err_xsessions_open = error en obrir la carpeta de sessions
|
||||||
|
hibernate = hibernar
|
||||||
insert = inserir
|
insert = inserir
|
||||||
login = iniciar sessió
|
login = iniciar sessió
|
||||||
logout = sessió tancada
|
logout = sessió tancada
|
||||||
no_x11_support = el suport per x11 ha estat desactivat en la compilació
|
no_x11_support = suport x11 desactivat en temps de compilació
|
||||||
normal = normal
|
normal = normal
|
||||||
numlock = Bloq Num
|
numlock = Bloq Num
|
||||||
|
other = altres
|
||||||
password = Clau
|
password = Clau
|
||||||
restart = reiniciar
|
restart = reiniciar
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = aturar
|
shutdown = aturar
|
||||||
sleep = suspendre
|
sleep = suspendre
|
||||||
|
toggle_password = mostrar/amagar contrasenya
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
x11 = x11
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,33 @@
|
||||||
|
authenticating = ověřování...
|
||||||
|
brightness_down = snížit jas
|
||||||
|
brightness_up = zvýšit jas
|
||||||
capslock = capslock
|
capslock = capslock
|
||||||
|
custom = vlastní
|
||||||
|
custom_info_err_output_long = výstup je příliš dlouhý
|
||||||
|
custom_info_err_no_output = žádný výstup
|
||||||
|
custom_info_err_no_output_error = , možná chyba
|
||||||
err_alloc = alokace paměti selhala
|
err_alloc = alokace paměti selhala
|
||||||
|
err_args = nelze analyzovat argumenty příkazového řádku
|
||||||
|
err_autologin_session = relace automatického přihlášení nebyla nalezena
|
||||||
err_bounds = index je mimo hranice pole
|
err_bounds = index je mimo hranice pole
|
||||||
|
err_brightness_change = nepodařilo se změnit jas
|
||||||
err_chdir = nelze otevřít domovský adresář
|
err_chdir = nelze otevřít domovský adresář
|
||||||
|
err_clock_too_long = řetězec hodin je příliš dlouhý
|
||||||
|
err_config = nelze analyzovat konfigurační soubor
|
||||||
|
err_crawl = nepodařilo se prohledat adresáře relací
|
||||||
err_dgn_oob = zpráva protokolu
|
err_dgn_oob = zpráva protokolu
|
||||||
err_domain = neplatná doména
|
err_domain = neplatná doména
|
||||||
|
err_empty_password = prázdné heslo není povoleno
|
||||||
|
err_envlist = nepodařilo se získat seznam proměnných prostředí
|
||||||
|
err_get_active_tty = nepodařilo se získat aktivní tty
|
||||||
|
err_hibernate = nepodařilo se spustit příkaz hibernace
|
||||||
err_hostname = nelze získat název hostitele
|
err_hostname = nelze získat název hostitele
|
||||||
|
err_inactivity = nepodařilo se spustit příkaz nečinnosti
|
||||||
|
err_lock_state = nepodařilo se získat stav zámku
|
||||||
|
err_log = nepodařilo se otevřít soubor protokolu
|
||||||
err_mlock = uzamčení paměti hesel selhalo
|
err_mlock = uzamčení paměti hesel selhalo
|
||||||
err_null = nulový ukazatel
|
err_null = nulový ukazatel
|
||||||
|
err_numlock = nepodařilo se nastavit numlock
|
||||||
err_pam = pam transakce selhala
|
err_pam = pam transakce selhala
|
||||||
err_pam_abort = pam transakce přerušena
|
err_pam_abort = pam transakce přerušena
|
||||||
err_pam_acct_expired = platnost účtu vypršela
|
err_pam_acct_expired = platnost účtu vypršela
|
||||||
|
|
@ -46,33 +49,34 @@ err_perm_dir = nepodařilo se změnit adresář
|
||||||
err_perm_group = nepodařilo se snížit skupinová oprávnění
|
err_perm_group = nepodařilo se snížit skupinová oprávnění
|
||||||
err_perm_user = nepodařilo se snížit uživatelská oprávnění
|
err_perm_user = nepodařilo se snížit uživatelská oprávnění
|
||||||
err_pwnam = nelze získat informace o uživateli
|
err_pwnam = nelze získat informace o uživateli
|
||||||
|
err_sleep = nepodařilo se spustit příkaz spánku
|
||||||
|
err_start = nepodařilo se spustit příkaz spuštění
|
||||||
|
err_battery = nepodařilo se načíst stav baterie
|
||||||
|
err_switch_tty = nepodařilo se přepnout tty
|
||||||
|
err_tty_ctrl = přenos řízení tty selhal
|
||||||
|
err_no_users = nebyli nalezeni žádní uživatelé
|
||||||
|
err_uid_range = nepodařilo se dynamicky získat rozsah uid
|
||||||
err_user_gid = nastavení GID uživatele selhalo
|
err_user_gid = nastavení GID uživatele selhalo
|
||||||
err_user_init = inicializace uživatele selhala
|
err_user_init = inicializace uživatele selhala
|
||||||
err_user_uid = nastavení UID uživateli selhalo
|
err_user_uid = nastavení UID uživateli selhalo
|
||||||
|
err_xauth = příkaz xauth selhal
|
||||||
|
err_xcb_conn = připojení xcb 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í
|
||||||
|
hibernate = hibernace
|
||||||
|
insert = vložit
|
||||||
login = uživatel
|
login = uživatel
|
||||||
logout = odhlášen
|
logout = odhlášen
|
||||||
|
no_x11_support = podpora x11 zakázána při kompilaci
|
||||||
|
normal = normální
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
|
other = jiné
|
||||||
password = heslo
|
password = heslo
|
||||||
restart = restartovat
|
restart = restartovat
|
||||||
shell = příkazový řádek
|
shell = příkazový řádek
|
||||||
shutdown = vypnout
|
shutdown = vypnout
|
||||||
|
sleep = uspat
|
||||||
|
toggle_password = zobrazit/skrýt heslo
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -2,26 +2,29 @@ authenticating = authentifizieren...
|
||||||
brightness_down = Helligkeit-
|
brightness_down = Helligkeit-
|
||||||
brightness_up = Helligkeit+
|
brightness_up = Helligkeit+
|
||||||
capslock = Feststelltaste
|
capslock = Feststelltaste
|
||||||
|
custom = benutzerdefiniert
|
||||||
|
custom_info_err_output_long = Ausgabe zu lang
|
||||||
|
custom_info_err_no_output = keine Ausgabe
|
||||||
|
custom_info_err_no_output_error = , möglicher Fehler
|
||||||
err_alloc = Speicherzuweisung fehlgeschlagen
|
err_alloc = Speicherzuweisung fehlgeschlagen
|
||||||
|
err_args = Kommandozeilenargumente konnten nicht verarbeitet werden
|
||||||
|
err_autologin_session = Autologin-Sitzung nicht gefunden
|
||||||
err_bounds = Index ausserhalb des Bereichs
|
err_bounds = Index ausserhalb des Bereichs
|
||||||
err_brightness_change = Helligkeitsänderung fehlgeschlagen
|
err_brightness_change = Helligkeitsänderung fehlgeschlagen
|
||||||
err_chdir = Fehler beim Oeffnen des Home-Ordners
|
err_chdir = Fehler beim Oeffnen des Home-Ordners
|
||||||
|
err_clock_too_long = Uhrzeitzeichenkette zu lang
|
||||||
err_config = Fehler beim Verarbeiten der Konfigurationsdatei
|
err_config = Fehler beim Verarbeiten der Konfigurationsdatei
|
||||||
|
err_crawl = Sitzungsverzeichnisse konnten nicht durchsucht werden
|
||||||
err_dgn_oob = Diagnose-Nachricht
|
err_dgn_oob = Diagnose-Nachricht
|
||||||
err_domain = Ungueltige Domain
|
err_domain = Ungueltige Domain
|
||||||
err_empty_password = Leeres Passwort nicht zugelassen
|
err_empty_password = Leeres Passwort nicht zugelassen
|
||||||
err_envlist = Fehler beim Abrufen der Umgebungs-Variablen
|
err_envlist = Fehler beim Abrufen der Umgebungs-Variablen
|
||||||
|
err_get_active_tty = Aktives tty konnte nicht ermittelt werden
|
||||||
|
err_hibernate = Ruhezustand-Befehl konnte nicht ausgeführt werden
|
||||||
err_hostname = Abrufen des Hostnames fehlgeschlagen
|
err_hostname = Abrufen des Hostnames fehlgeschlagen
|
||||||
|
err_inactivity = Inaktivitätsbefehl konnte nicht ausgeführt werden
|
||||||
|
err_lock_state = Sperrstatus konnte nicht ermittelt werden
|
||||||
|
err_log = Protokolldatei konnte nicht geöffnet werden
|
||||||
err_mlock = Sperren des Passwortspeichers fehlgeschlagen
|
err_mlock = Sperren des Passwortspeichers fehlgeschlagen
|
||||||
err_null = Null Pointer
|
err_null = Null Pointer
|
||||||
err_numlock = Numlock konnte nicht aktiviert werden
|
err_numlock = Numlock konnte nicht aktiviert werden
|
||||||
|
|
@ -47,12 +50,12 @@ err_perm_group = Fehler beim Heruntersetzen der Gruppenberechtigungen
|
||||||
err_perm_user = Fehler beim Heruntersetzen der Nutzerberechtigungen
|
err_perm_user = Fehler beim Heruntersetzen der Nutzerberechtigungen
|
||||||
err_pwnam = Abrufen der Benutzerinformationen fehlgeschlagen
|
err_pwnam = Abrufen der Benutzerinformationen fehlgeschlagen
|
||||||
err_sleep = Sleep-Befehl fehlgeschlagen
|
err_sleep = Sleep-Befehl fehlgeschlagen
|
||||||
|
err_start = Startbefehl konnte nicht ausgeführt werden
|
||||||
|
err_battery = Akkustand konnte nicht geladen werden
|
||||||
|
err_switch_tty = tty konnte nicht gewechselt werden
|
||||||
err_tty_ctrl = Fehler bei der TTY-Uebergabe
|
err_tty_ctrl = Fehler bei der TTY-Uebergabe
|
||||||
|
err_no_users = Keine Benutzer gefunden
|
||||||
|
err_uid_range = uid-Bereich konnte nicht dynamisch ermittelt werden
|
||||||
err_user_gid = Fehler beim Setzen der Gruppen-ID
|
err_user_gid = Fehler beim Setzen der Gruppen-ID
|
||||||
err_user_init = Nutzer-Initialisierung fehlgeschlagen
|
err_user_init = Nutzer-Initialisierung fehlgeschlagen
|
||||||
err_user_uid = Setzen der Benutzer-ID fehlgeschlagen
|
err_user_uid = Setzen der Benutzer-ID fehlgeschlagen
|
||||||
|
|
@ -60,11 +63,11 @@ err_xauth = Xauth-Befehl fehlgeschlagen
|
||||||
err_xcb_conn = xcb-Verbindung fehlgeschlagen
|
err_xcb_conn = xcb-Verbindung fehlgeschlagen
|
||||||
err_xsessions_dir = Fehler beim Finden des Sitzungsordners
|
err_xsessions_dir = Fehler beim Finden des Sitzungsordners
|
||||||
err_xsessions_open = Fehler beim Oeffnen des Sitzungsordners
|
err_xsessions_open = Fehler beim Oeffnen des Sitzungsordners
|
||||||
|
hibernate = Ruhezustand
|
||||||
insert = Einfügen
|
insert = Einfügen
|
||||||
login = Nutzer
|
login = Nutzer
|
||||||
logout = Abmelden
|
logout = Abmelden
|
||||||
no_x11_support = X11-Support bei Kompilierung deaktiviert
|
no_x11_support = x11-Unterstützung zur Kompilierzeit deaktiviert
|
||||||
normal = Normal
|
normal = Normal
|
||||||
numlock = Numlock
|
numlock = Numlock
|
||||||
other = Andere
|
other = Andere
|
||||||
|
|
@ -73,6 +76,7 @@ restart = Neustarten
|
||||||
shell = Shell
|
shell = Shell
|
||||||
shutdown = Herunterfahren
|
shutdown = Herunterfahren
|
||||||
sleep = Sleep
|
sleep = Sleep
|
||||||
|
toggle_password = Passwort anzeigen/verbergen
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
x11 = X11
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ brightness_down = decrease brightness
|
||||||
brightness_up = increase brightness
|
brightness_up = increase brightness
|
||||||
capslock = capslock
|
capslock = capslock
|
||||||
custom = custom
|
custom = custom
|
||||||
|
custom_info_err_output_long = output too long
|
||||||
|
custom_info_err_no_output = no output
|
||||||
|
custom_info_err_no_output_error = , possible error
|
||||||
err_alloc = failed memory allocation
|
err_alloc = failed memory allocation
|
||||||
err_args = unable to parse command line arguments
|
err_args = unable to parse command line arguments
|
||||||
err_autologin_session = autologin session not found
|
err_autologin_session = autologin session not found
|
||||||
|
|
@ -73,6 +76,7 @@ restart = reboot
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = shutdown
|
shutdown = shutdown
|
||||||
sleep = sleep
|
sleep = sleep
|
||||||
|
toggle_password = toggle password
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
x11 = x11
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
authenticating = aŭtentigado...
|
||||||
|
brightness_down = malpliigi helecon
|
||||||
|
brightness_up = pliigi helecon
|
||||||
|
capslock = majuskla baskulo
|
||||||
|
custom = propra
|
||||||
|
custom_info_err_output_long = eligo tro longa
|
||||||
|
custom_info_err_no_output = neniu eligo
|
||||||
|
custom_info_err_no_output_error = , ebla eraro
|
||||||
|
err_alloc = malsukcesis memorasignon
|
||||||
|
err_args = ne povas analizi argumentojn de komanda linio
|
||||||
|
err_autologin_session = aŭtomatan ensalutan seancon ne trovis
|
||||||
|
err_bounds = indico estas ekster-intervala
|
||||||
|
err_brightness_change = malsukcesis ŝanĝi la helecon
|
||||||
|
err_chdir = malsukcesis malfermi hejman dosierujon
|
||||||
|
err_clock_too_long = horloĝa ĉeno estas tro longa
|
||||||
|
err_config = ne povas analizi agordan dosieron
|
||||||
|
err_crawl = malsukcesis dum serĉado de seancaj dosierujoj
|
||||||
|
err_dgn_oob = protokola mesaĝo
|
||||||
|
err_domain = malvalida domajno
|
||||||
|
err_empty_password = ne akceptas malplenan pasvorton
|
||||||
|
err_envlist = malsukcesis preni la medivariablojn
|
||||||
|
err_get_active_tty = malsukcesis preni la aktivan TTY-on
|
||||||
|
err_hibernate = malsukcesis ruli la komandon por diskodormo
|
||||||
|
err_hostname = malsukcesis preni la sistemnomon
|
||||||
|
err_inactivity = malsukcesis ruli la agorditan komandon por malaktiveco
|
||||||
|
err_lock_state = malsukcesis preni la ŝlosan staton
|
||||||
|
err_log = malsukcesis malfermi la protokolan dosieron
|
||||||
|
err_mlock = malsukcesis ŝlosi pasvortan memoron
|
||||||
|
err_null = nula memorloko
|
||||||
|
err_numlock = malsukcesis agordi numeran baskulon
|
||||||
|
err_pam = PAM-a transakcio malsukcesis
|
||||||
|
err_pam_abort = PAM-a transakcio malsukcesis
|
||||||
|
err_pam_acct_expired = konto eksvalidiĝis
|
||||||
|
err_pam_auth = aŭtentiga eraro
|
||||||
|
err_pam_authinfo_unavail = malsukcesis preni uzantajn informojn
|
||||||
|
err_pam_authok_reqd = memorsigno eksvalidiĝis
|
||||||
|
err_pam_buf = bufra eraro
|
||||||
|
err_pam_cred_err = malsukcesis agordi akreditaĵon
|
||||||
|
err_pam_cred_expired = akreditaĵo eksvalidiĝis
|
||||||
|
err_pam_cred_insufficient = nesufiĉa akreditaĵo
|
||||||
|
err_pam_cred_unavail = malsukcesis preni akreditaĵon
|
||||||
|
err_pam_maxtries = atingis maksimuman kvanton da provoj
|
||||||
|
err_pam_perm_denied = permeso negis
|
||||||
|
err_pam_session = seancan eraron
|
||||||
|
err_pam_sys = sisteman eraron
|
||||||
|
err_pam_user_unknown = ne konas uzanton
|
||||||
|
err_path = malsukcesis agordi la median dosierindikon
|
||||||
|
err_perm_dir = malsukcesis ŝanĝi la nunan dosierujon
|
||||||
|
err_perm_group = malsukcesis redukti grupajn permesojn
|
||||||
|
err_perm_user = malsukcesis redukti uzantajn permesojn
|
||||||
|
err_pwnam = malsukcesis preni uzantajn informojn
|
||||||
|
err_sleep = malsukcesis ruli memordorman komandon
|
||||||
|
err_start = malsukcesis ruli startan komandon
|
||||||
|
err_battery = malsukcesis ŝargi baterian staton
|
||||||
|
err_switch_tty = malsukcesis ŝanĝi TTY-on
|
||||||
|
err_tty_ctrl = TTY-an stiran transigon malsukcesis
|
||||||
|
err_no_users = nul uzantojn trovas
|
||||||
|
err_uid_range = malsukcesis dinamike preni UID-an intervalon
|
||||||
|
err_user_gid = malsukcesis agordi uzantan GID-on
|
||||||
|
err_user_init = malsukcesis iniciĝi uzanto
|
||||||
|
err_user_uid = malsukcesis agordi uzantan UID-on
|
||||||
|
err_xauth = malsukcesis plenumi je xauth
|
||||||
|
err_xcb_conn = malsukcesis dum konectado al xcb
|
||||||
|
err_xsessions_dir = malsukcesis trovi seancan dosierujon
|
||||||
|
err_xsessions_open = malsukcesis malfermi seancan dosierujon
|
||||||
|
hibernate = diskodormi
|
||||||
|
insert = enmeti
|
||||||
|
login = uzanto
|
||||||
|
logout = elsalutis
|
||||||
|
no_x11_support = x11 estas foriĝita de kompil-tempo
|
||||||
|
normal = normala
|
||||||
|
numlock = numera baskulo
|
||||||
|
other = alia
|
||||||
|
password = pasvorto
|
||||||
|
restart = restartigi
|
||||||
|
shell = ŝelo
|
||||||
|
shutdown = malŝalti
|
||||||
|
sleep = memordormi
|
||||||
|
toggle_password = montri/kaŝi pasvorton
|
||||||
|
wayland = wayland
|
||||||
|
x11 = x11
|
||||||
|
xinitrc = xinitrc
|
||||||
|
|
@ -2,29 +2,32 @@ authenticating = autenticando...
|
||||||
brightness_down = bajar brillo
|
brightness_down = bajar brillo
|
||||||
brightness_up = subir brillo
|
brightness_up = subir brillo
|
||||||
capslock = Bloq Mayús
|
capslock = Bloq Mayús
|
||||||
|
custom = personalizado
|
||||||
|
custom_info_err_output_long = salida demasiado larga
|
||||||
|
custom_info_err_no_output = sin salida
|
||||||
|
custom_info_err_no_output_error = , posible error
|
||||||
err_alloc = asignación de memoria fallida
|
err_alloc = asignación de memoria fallida
|
||||||
|
err_args = no se pudieron analizar los argumentos de la línea de comandos
|
||||||
|
err_autologin_session = no se encontró la sesión de inicio de sesión automático
|
||||||
err_bounds = índice fuera de límites
|
err_bounds = índice fuera de límites
|
||||||
|
err_brightness_change = no se pudo cambiar el brillo
|
||||||
err_chdir = error al abrir la carpeta home
|
err_chdir = error al abrir la carpeta home
|
||||||
|
err_clock_too_long = la cadena del reloj es demasiado larga
|
||||||
|
err_config = no se pudo analizar el archivo de configuración
|
||||||
|
err_crawl = no se pudieron explorar los directorios de sesión
|
||||||
err_dgn_oob = mensaje de registro
|
err_dgn_oob = mensaje de registro
|
||||||
err_domain = dominio inválido
|
err_domain = dominio inválido
|
||||||
|
err_empty_password = no se permite contraseña vacía
|
||||||
|
err_envlist = no se pudo obtener la lista de variables de entorno
|
||||||
|
err_get_active_tty = no se pudo obtener el tty activo
|
||||||
|
err_hibernate = no se pudo ejecutar el comando de hibernación
|
||||||
err_hostname = error al obtener el nombre de host
|
err_hostname = error al obtener el nombre de host
|
||||||
|
err_inactivity = no se pudo ejecutar el comando de inactividad
|
||||||
|
err_lock_state = no se pudo obtener el estado de bloqueo
|
||||||
|
err_log = no se pudo abrir el archivo de registro
|
||||||
err_mlock = error al bloquear la contraseña de memoria
|
err_mlock = error al bloquear la contraseña de memoria
|
||||||
err_null = puntero nulo
|
err_null = puntero nulo
|
||||||
|
err_numlock = no se pudo configurar numlock
|
||||||
err_pam = error en la transacción pam
|
err_pam = error en la transacción pam
|
||||||
err_pam_abort = transacción pam abortada
|
err_pam_abort = transacción pam abortada
|
||||||
err_pam_acct_expired = cuenta expirada
|
err_pam_acct_expired = cuenta expirada
|
||||||
|
|
@ -46,25 +49,25 @@ err_perm_dir = error al cambiar el directorio actual
|
||||||
err_perm_group = error al degradar los permisos del grupo
|
err_perm_group = error al degradar los permisos del grupo
|
||||||
err_perm_user = error al degradar los permisos del usuario
|
err_perm_user = error al degradar los permisos del usuario
|
||||||
err_pwnam = error al obtener la información del usuario
|
err_pwnam = error al obtener la información del usuario
|
||||||
|
err_sleep = no se pudo ejecutar el comando de suspensión
|
||||||
|
err_start = no se pudo ejecutar el comando de inicio
|
||||||
|
err_battery = no se pudo cargar el estado de la batería
|
||||||
|
err_switch_tty = no se pudo cambiar de tty
|
||||||
|
err_tty_ctrl = falló la transferencia de control tty
|
||||||
|
err_no_users = no se encontraron usuarios
|
||||||
|
err_uid_range = no se pudo obtener dinámicamente el rango de uid
|
||||||
err_user_gid = error al establecer el GID del usuario
|
err_user_gid = error al establecer el GID del usuario
|
||||||
err_user_init = error al inicializar usuario
|
err_user_init = error al inicializar usuario
|
||||||
err_user_uid = error al establecer el UID del usuario
|
err_user_uid = error al establecer el UID del usuario
|
||||||
|
err_xauth = falló el comando xauth
|
||||||
|
err_xcb_conn = falló la conexión xcb
|
||||||
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
|
||||||
|
hibernate = hibernar
|
||||||
insert = insertar
|
insert = insertar
|
||||||
login = usuario
|
login = usuario
|
||||||
logout = cerrar sesión
|
logout = cerrar sesión
|
||||||
no_x11_support = soporte para x11 deshabilitado en tiempo de compilación
|
no_x11_support = soporte x11 desactivado en tiempo de compilación
|
||||||
normal = normal
|
normal = normal
|
||||||
numlock = Bloq Num
|
numlock = Bloq Num
|
||||||
other = otro
|
other = otro
|
||||||
|
|
@ -73,6 +76,7 @@ restart = reiniciar
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = apagar
|
shutdown = apagar
|
||||||
sleep = suspender
|
sleep = suspender
|
||||||
|
toggle_password = mostrar/ocultar contraseña
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ brightness_down = diminuer la luminosité
|
||||||
brightness_up = augmenter la luminosité
|
brightness_up = augmenter la luminosité
|
||||||
capslock = verr.maj
|
capslock = verr.maj
|
||||||
custom = customisé
|
custom = customisé
|
||||||
|
custom_info_err_output_long = sortie trop longue
|
||||||
|
custom_info_err_no_output = pas de sortie
|
||||||
|
custom_info_err_no_output_error = , erreur possible
|
||||||
err_alloc = échec d'allocation mémoire
|
err_alloc = échec d'allocation mémoire
|
||||||
err_args = échec de l'analyse des arguments en lignes de commande
|
err_args = échec de l'analyse des arguments en lignes de commande
|
||||||
err_autologin_session = session de connexion automatique introuvable
|
err_autologin_session = session de connexion automatique introuvable
|
||||||
|
|
@ -73,6 +76,7 @@ restart = redémarrer
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = éteindre
|
shutdown = éteindre
|
||||||
sleep = veille
|
sleep = veille
|
||||||
|
toggle_password = afficher le mot de passe
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
x11 = x11
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,33 @@
|
||||||
|
authenticating = autenticazione in corso...
|
||||||
|
brightness_down = diminuisci luminosità
|
||||||
|
brightness_up = aumenta luminosità
|
||||||
capslock = capslock
|
capslock = capslock
|
||||||
|
custom = personalizzato
|
||||||
|
custom_info_err_output_long = output troppo lungo
|
||||||
|
custom_info_err_no_output = nessun output
|
||||||
|
custom_info_err_no_output_error = , possibile errore
|
||||||
err_alloc = impossibile allocare memoria
|
err_alloc = impossibile allocare memoria
|
||||||
|
err_args = impossibile analizzare gli argomenti della riga di comando
|
||||||
|
err_autologin_session = sessione di accesso automatico non trovata
|
||||||
err_bounds = indice fuori limite
|
err_bounds = indice fuori limite
|
||||||
|
err_brightness_change = impossibile modificare la luminosità
|
||||||
err_chdir = impossibile aprire home directory
|
err_chdir = impossibile aprire home directory
|
||||||
|
err_clock_too_long = stringa dell'orologio troppo lunga
|
||||||
|
err_config = impossibile analizzare il file di configurazione
|
||||||
|
err_crawl = impossibile esplorare le directory delle sessioni
|
||||||
err_dgn_oob = messaggio log
|
err_dgn_oob = messaggio log
|
||||||
err_domain = dominio non valido
|
err_domain = dominio non valido
|
||||||
|
err_empty_password = password vuota non consentita
|
||||||
|
err_envlist = impossibile ottenere la lista delle variabili d'ambiente
|
||||||
|
err_get_active_tty = impossibile ottenere il tty attivo
|
||||||
|
err_hibernate = impossibile eseguire il comando di ibernazione
|
||||||
err_hostname = impossibile ottenere hostname
|
err_hostname = impossibile ottenere hostname
|
||||||
|
err_inactivity = impossibile eseguire il comando di inattività
|
||||||
|
err_lock_state = impossibile ottenere lo stato di blocco
|
||||||
|
err_log = impossibile aprire il file di log
|
||||||
err_mlock = impossibile ottenere lock per la password in memoria
|
err_mlock = impossibile ottenere lock per la password in memoria
|
||||||
err_null = puntatore nullo
|
err_null = puntatore nullo
|
||||||
|
err_numlock = impossibile impostare il numlock
|
||||||
err_pam = transazione PAM fallita
|
err_pam = transazione PAM fallita
|
||||||
err_pam_abort = transazione PAM interrotta
|
err_pam_abort = transazione PAM interrotta
|
||||||
err_pam_acct_expired = account scaduto
|
err_pam_acct_expired = account scaduto
|
||||||
|
|
@ -46,33 +49,34 @@ err_perm_dir = impossibile cambiare directory corrente
|
||||||
err_perm_group = impossibile ridurre permessi gruppo
|
err_perm_group = impossibile ridurre permessi gruppo
|
||||||
err_perm_user = impossibile ridurre permessi utente
|
err_perm_user = impossibile ridurre permessi utente
|
||||||
err_pwnam = impossibile ottenere dati utente
|
err_pwnam = impossibile ottenere dati utente
|
||||||
|
err_sleep = impossibile eseguire il comando di sospensione
|
||||||
|
err_start = impossibile eseguire il comando di avvio
|
||||||
|
err_battery = impossibile caricare lo stato della batteria
|
||||||
|
err_switch_tty = impossibile cambiare tty
|
||||||
|
err_tty_ctrl = trasferimento del controllo tty fallito
|
||||||
|
err_no_users = nessun utente trovato
|
||||||
|
err_uid_range = impossibile ottenere dinamicamente l'intervallo uid
|
||||||
err_user_gid = impossibile impostare GID utente
|
err_user_gid = impossibile impostare GID utente
|
||||||
err_user_init = impossibile inizializzare utente
|
err_user_init = impossibile inizializzare utente
|
||||||
err_user_uid = impossible impostare UID utente
|
err_user_uid = impossible impostare UID utente
|
||||||
|
err_xauth = comando xauth fallito
|
||||||
|
err_xcb_conn = connessione xcb fallita
|
||||||
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
|
||||||
|
hibernate = ibernazione
|
||||||
|
insert = inserisci
|
||||||
login = username
|
login = username
|
||||||
logout = scollegato
|
logout = scollegato
|
||||||
|
no_x11_support = supporto x11 disabilitato in fase di compilazione
|
||||||
|
normal = normale
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
|
other = altro
|
||||||
password = password
|
password = password
|
||||||
restart = riavvio
|
restart = riavvio
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = arresto
|
shutdown = arresto
|
||||||
|
sleep = sospendi
|
||||||
|
toggle_password = mostra/nascondi password
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -2,26 +2,29 @@ authenticating = 認証中...
|
||||||
brightness_down = 明るさを下げる
|
brightness_down = 明るさを下げる
|
||||||
brightness_up = 明るさを上げる
|
brightness_up = 明るさを上げる
|
||||||
capslock = CapsLock
|
capslock = CapsLock
|
||||||
|
custom = カスタム
|
||||||
|
custom_info_err_output_long = 出力が長すぎます
|
||||||
|
custom_info_err_no_output = 出力なし
|
||||||
|
custom_info_err_no_output_error = 、エラーの可能性あり
|
||||||
err_alloc = メモリ割り当て失敗
|
err_alloc = メモリ割り当て失敗
|
||||||
|
err_args = コマンドライン引数を解析できません
|
||||||
|
err_autologin_session = 自動ログインセッションが見つかりません
|
||||||
err_bounds = 境界外インデックス
|
err_bounds = 境界外インデックス
|
||||||
err_brightness_change = 明るさの変更に失敗しました
|
err_brightness_change = 明るさの変更に失敗しました
|
||||||
err_chdir = ホームフォルダを開けませんでした
|
err_chdir = ホームフォルダを開けませんでした
|
||||||
|
err_clock_too_long = 時計の文字列が長すぎます
|
||||||
err_config = 設定ファイルを解析できません
|
err_config = 設定ファイルを解析できません
|
||||||
|
err_crawl = セッションディレクトリのクロールに失敗しました
|
||||||
err_dgn_oob = ログメッセージ
|
err_dgn_oob = ログメッセージ
|
||||||
err_domain = 無効なドメイン
|
err_domain = 無効なドメイン
|
||||||
err_empty_password = 空のパスワードは許可されていません
|
err_empty_password = 空のパスワードは許可されていません
|
||||||
err_envlist = 環境変数リストの取得に失敗しました
|
err_envlist = 環境変数リストの取得に失敗しました
|
||||||
|
err_get_active_tty = アクティブなttyの取得に失敗しました
|
||||||
|
err_hibernate = 休止状態コマンドの実行に失敗しました
|
||||||
err_hostname = ホスト名の取得に失敗しました
|
err_hostname = ホスト名の取得に失敗しました
|
||||||
|
err_inactivity = 無操作コマンドの実行に失敗しました
|
||||||
|
err_lock_state = ロック状態の取得に失敗しました
|
||||||
|
err_log = ログファイルを開けませんでした
|
||||||
err_mlock = パスワードメモリのロックに失敗しました
|
err_mlock = パスワードメモリのロックに失敗しました
|
||||||
err_null = ヌルポインタ
|
err_null = ヌルポインタ
|
||||||
err_numlock = NumLockの設定に失敗しました
|
err_numlock = NumLockの設定に失敗しました
|
||||||
|
|
@ -47,12 +50,12 @@ err_perm_group = グループ権限のダウングレードに失敗しました
|
||||||
err_perm_user = ユーザー権限のダウングレードに失敗しました
|
err_perm_user = ユーザー権限のダウングレードに失敗しました
|
||||||
err_pwnam = ユーザー情報の取得に失敗しました
|
err_pwnam = ユーザー情報の取得に失敗しました
|
||||||
err_sleep = スリープコマンドの実行に失敗しました
|
err_sleep = スリープコマンドの実行に失敗しました
|
||||||
|
err_start = 起動コマンドの実行に失敗しました
|
||||||
|
err_battery = バッテリー状態の読み込みに失敗しました
|
||||||
|
err_switch_tty = ttyの切り替えに失敗しました
|
||||||
err_tty_ctrl = TTY制御の転送に失敗しました
|
err_tty_ctrl = TTY制御の転送に失敗しました
|
||||||
|
err_no_users = ユーザーが見つかりません
|
||||||
|
err_uid_range = uidの範囲を動的に取得できませんでした
|
||||||
err_user_gid = ユーザーGIDの設定に失敗しました
|
err_user_gid = ユーザーGIDの設定に失敗しました
|
||||||
err_user_init = ユーザーの初期化に失敗しました
|
err_user_init = ユーザーの初期化に失敗しました
|
||||||
err_user_uid = ユーザーUIDの設定に失敗しました
|
err_user_uid = ユーザーUIDの設定に失敗しました
|
||||||
|
|
@ -60,11 +63,11 @@ err_xauth = xauthコマンドの実行に失敗しました
|
||||||
err_xcb_conn = XCB接続に失敗しました
|
err_xcb_conn = XCB接続に失敗しました
|
||||||
err_xsessions_dir = セッションフォルダが見つかりませんでした
|
err_xsessions_dir = セッションフォルダが見つかりませんでした
|
||||||
err_xsessions_open = セッションフォルダを開けませんでした
|
err_xsessions_open = セッションフォルダを開けませんでした
|
||||||
|
hibernate = 休止状態
|
||||||
insert = 挿入
|
insert = 挿入
|
||||||
login = ログイン
|
login = ログイン
|
||||||
logout = ログアウト済み
|
logout = ログアウト済み
|
||||||
no_x11_support = X11サポートはコンパイル時に無効化されています
|
no_x11_support = x11サポートはコンパイル時に無効化されています
|
||||||
normal = 通常
|
normal = 通常
|
||||||
numlock = NumLock
|
numlock = NumLock
|
||||||
other = その他
|
other = その他
|
||||||
|
|
@ -73,6 +76,7 @@ restart = 再起動
|
||||||
shell = シェル
|
shell = シェル
|
||||||
shutdown = シャットダウン
|
shutdown = シャットダウン
|
||||||
sleep = スリープ
|
sleep = スリープ
|
||||||
|
toggle_password = パスワードの表示/非表示
|
||||||
wayland = Wayland
|
wayland = Wayland
|
||||||
x11 = X11
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
authenticating = tê piştrastkirin...
|
||||||
|
brightness_down = ronahiyê kêm bike
|
||||||
|
brightness_up = ronahiyê bilind bike
|
||||||
|
capslock = tîpên girdek (capslock)
|
||||||
|
custom = kesane
|
||||||
|
custom_info_err_output_long = encam pir dirêj e
|
||||||
|
custom_info_err_no_output = encam tune
|
||||||
|
custom_info_err_no_output_error = , xeletiya mimkun
|
||||||
|
err_alloc = veqetandina bîrê têk çû
|
||||||
|
err_args = argumanên rêzika fermanê nehatin analîzkirin
|
||||||
|
err_autologin_session = danişîna têketina xweber nehate dîtin
|
||||||
|
err_bounds = îndeksa derveyî sînor
|
||||||
|
err_brightness_change = guherandina ronahiyê têk çû
|
||||||
|
err_chdir = vekirina peldanka malê têk çû
|
||||||
|
err_clock_too_long = rêzika demjimêrê pir dirêj e
|
||||||
|
err_config = pela rêkxistinê nehat analîzkirin
|
||||||
|
err_crawl = gerandina pelrêçên danişînê têk çû
|
||||||
|
err_dgn_oob = peyama têketinê
|
||||||
|
err_domain = navpara nederbasdar
|
||||||
|
err_empty_password = borînpeyv nabe ku vala be
|
||||||
|
err_envlist = girtina lîsteya jîngehê (envlist) têk çû
|
||||||
|
err_get_active_tty = girtina tty ya çalak têk çû
|
||||||
|
err_hibernate = fermana cemidaninê nehat xebitandin
|
||||||
|
err_hostname = girtina navê mêvandar têk çû
|
||||||
|
err_inactivity = fermana neçalaktiyê nehat xebitandin
|
||||||
|
err_lock_state = girtina rewşa kilîtkirinê têk çû
|
||||||
|
err_log = vekirina pelê têkeinê têk çû
|
||||||
|
err_mlock = kilîtkirina bîra borînpeyvê têk çû
|
||||||
|
err_null = nîşandera null
|
||||||
|
err_numlock = sazkirina numlock têk çû
|
||||||
|
err_pam = danûstendina pam têk çû
|
||||||
|
err_pam_abort = danûstendina pam hate têkbirin
|
||||||
|
err_pam_acct_expired = dema jimarê derbas bûye
|
||||||
|
err_pam_auth = şaşetiya piştrastkirinê
|
||||||
|
err_pam_authinfo_unavail = zanyariyên bikarhêner nehatin girtin
|
||||||
|
err_pam_authok_reqd = dema nîşandanê derbas bûye
|
||||||
|
err_pam_buf = şaşetiya bîra demkî
|
||||||
|
err_pam_cred_err = sazkirina rastkitinê têk çû
|
||||||
|
err_pam_cred_expired = dema rastkitinê derbas bûye
|
||||||
|
err_pam_cred_insufficient = rastkitinê kêm
|
||||||
|
err_pam_cred_unavail = girtina rastkitinê têk çû
|
||||||
|
err_pam_maxtries = sînorê hewldanên herî bilind hat gihîştin
|
||||||
|
err_pam_perm_denied = mafdayîn hat paşguhkirin
|
||||||
|
err_pam_session = şaşetiya danişînê
|
||||||
|
err_pam_sys = şaşetiya pergalê
|
||||||
|
err_pam_user_unknown = bikarhênerê nenas
|
||||||
|
err_path = sazkirina rêgehê têk çû
|
||||||
|
err_perm_dir = guhertina pelrêçê heyî têk çû
|
||||||
|
err_perm_group = kêmkirina mafdayînên komê têk çû
|
||||||
|
err_perm_user = kêmkirina mafdayînên bikarhêner têk çû
|
||||||
|
err_pwnam = girtina zanyariyên bikarhêner têk çû
|
||||||
|
err_sleep = fermana cemidaninê nehat xebitandin
|
||||||
|
err_start = fermana destpêkirinê nehat xebitandin
|
||||||
|
err_battery = barkirina rewşa betariyê têk çû
|
||||||
|
err_switch_tty = guhertina tty têk çû
|
||||||
|
err_tty_ctrl = guhertina kontrola tty têk çû
|
||||||
|
err_no_users = tu bikarhêner nehatin dîtin
|
||||||
|
err_uid_range = girtina rêjeya dînamîk a sînorê uid têk çû
|
||||||
|
err_user_gid = sazkirina GID a bikarhêner têk çû
|
||||||
|
err_user_init = destpêkirina bikarhêner têk çû
|
||||||
|
err_user_uid = sazkirina UID a bikarhêner têk çû
|
||||||
|
err_xauth = fermana xauth têk çû
|
||||||
|
err_xcb_conn = girêdana xcb têk çû
|
||||||
|
err_xsessions_dir = dîtina peldanka danişînan têk çû
|
||||||
|
err_xsessions_open = vekirina peldanka danişînan têk çû
|
||||||
|
hibernate = bicemidîne
|
||||||
|
insert = têxîne
|
||||||
|
login = têketin
|
||||||
|
logout = derkeve
|
||||||
|
no_x11_support = piştgiriya x11 di dema berhevkirinê de hatiye girtin
|
||||||
|
normal = normal
|
||||||
|
numlock = numlock
|
||||||
|
other = ên din
|
||||||
|
password = borînpeyv
|
||||||
|
restart = ji nû ve bide destpêkirin
|
||||||
|
shell = shell
|
||||||
|
shutdown = vemirîne
|
||||||
|
sleep = têxîne xewê
|
||||||
|
toggle_password = şîfre nîşan bide/veşêre
|
||||||
|
wayland = wayland
|
||||||
|
x11 = x11
|
||||||
|
xinitrc = xinitrc
|
||||||
|
|
@ -3,23 +3,26 @@ brightness_down = samazināt spilgtumu
|
||||||
brightness_up = palielināt spilgtumu
|
brightness_up = palielināt spilgtumu
|
||||||
capslock = caps lock
|
capslock = caps lock
|
||||||
custom = pielāgots
|
custom = pielāgots
|
||||||
|
custom_info_err_output_long = izvade pārāk gara
|
||||||
|
custom_info_err_no_output = nav izvades
|
||||||
|
custom_info_err_no_output_error = , iespējama kļūda
|
||||||
err_alloc = neizdevās atmiņas piešķiršana
|
err_alloc = neizdevās atmiņas piešķiršana
|
||||||
|
err_args = nevar parsēt komandrindas argumentus
|
||||||
|
err_autologin_session = automātiskās pieteikšanās sesija nav atrasta
|
||||||
err_bounds = indekss ārpus robežām
|
err_bounds = indekss ārpus robežām
|
||||||
err_brightness_change = neizdevās mainīt spilgtumu
|
err_brightness_change = neizdevās mainīt spilgtumu
|
||||||
err_chdir = neizdevās atvērt mājas mapi
|
err_chdir = neizdevās atvērt mājas mapi
|
||||||
err_clock_too_long = pulksteņa virkne pārāk gara
|
err_clock_too_long = pulksteņa virkne pārāk gara
|
||||||
err_config = neizdevās parsēt konfigurācijas failu
|
err_config = neizdevās parsēt konfigurācijas failu
|
||||||
|
err_crawl = neizdevās pārlūkot sesiju direktorijus
|
||||||
err_dgn_oob = žurnāla ziņojums
|
err_dgn_oob = žurnāla ziņojums
|
||||||
err_domain = nederīgs domēns
|
err_domain = nederīgs domēns
|
||||||
err_empty_password = tukša parole nav atļauta
|
err_empty_password = tukša parole nav atļauta
|
||||||
err_envlist = neizdevās iegūt vides mainīgo sarakstu
|
err_envlist = neizdevās iegūt vides mainīgo sarakstu
|
||||||
err_get_active_tty = neizdevās iegūt aktīvo tty
|
err_get_active_tty = neizdevās iegūt aktīvo tty
|
||||||
|
err_hibernate = neizdevās izpildīt hibernācijas komandu
|
||||||
err_hostname = neizdevās iegūt hostname
|
err_hostname = neizdevās iegūt hostname
|
||||||
|
err_inactivity = neizdevās izpildīt neaktivitātes komandu
|
||||||
err_lock_state = neizdevās iegūt bloķēšanas stāvokli
|
err_lock_state = neizdevās iegūt bloķēšanas stāvokli
|
||||||
err_log = neizdevās atvērt žurnāla failu
|
err_log = neizdevās atvērt žurnāla failu
|
||||||
err_mlock = neizdevās bloķēt paroles atmiņu
|
err_mlock = neizdevās bloķēt paroles atmiņu
|
||||||
|
|
@ -47,12 +50,12 @@ err_perm_group = neizdevās pazemināt grupas atļaujas
|
||||||
err_perm_user = neizdevās pazemināt lietotāja atļaujas
|
err_perm_user = neizdevās pazemināt lietotāja atļaujas
|
||||||
err_pwnam = neizdevās iegūt lietotāja informāciju
|
err_pwnam = neizdevās iegūt lietotāja informāciju
|
||||||
err_sleep = neizdevās izpildīt miega komandu
|
err_sleep = neizdevās izpildīt miega komandu
|
||||||
|
err_start = neizdevās izpildīt startēšanas komandu
|
||||||
err_battery = neizdevās ielādēt akumulatora stāvokli
|
err_battery = neizdevās ielādēt akumulatora stāvokli
|
||||||
err_switch_tty = neizdevās pārslēgt tty
|
err_switch_tty = neizdevās pārslēgt tty
|
||||||
err_tty_ctrl = tty vadības nodošana neizdevās
|
err_tty_ctrl = tty vadības nodošana neizdevās
|
||||||
err_no_users = lietotāji nav atrasti
|
err_no_users = lietotāji nav atrasti
|
||||||
|
err_uid_range = neizdevās dinamiski iegūt uid diapazonu
|
||||||
err_user_gid = neizdevās iestatīt lietotāja GID
|
err_user_gid = neizdevās iestatīt lietotāja GID
|
||||||
err_user_init = neizdevās inicializēt lietotāju
|
err_user_init = neizdevās inicializēt lietotāju
|
||||||
err_user_uid = neizdevās iestatīt lietotāja UID
|
err_user_uid = neizdevās iestatīt lietotāja UID
|
||||||
|
|
@ -60,7 +63,7 @@ err_xauth = xauth komanda neizdevās
|
||||||
err_xcb_conn = xcb savienojums neizdevās
|
err_xcb_conn = xcb savienojums neizdevās
|
||||||
err_xsessions_dir = neizdevās atrast sesiju mapi
|
err_xsessions_dir = neizdevās atrast sesiju mapi
|
||||||
err_xsessions_open = neizdevās atvērt sesiju mapi
|
err_xsessions_open = neizdevās atvērt sesiju mapi
|
||||||
|
hibernate = hibernācija
|
||||||
insert = ievietot
|
insert = ievietot
|
||||||
login = lietotājs
|
login = lietotājs
|
||||||
logout = iziet
|
logout = iziet
|
||||||
|
|
@ -73,6 +76,7 @@ restart = restartēt
|
||||||
shell = terminālis
|
shell = terminālis
|
||||||
shutdown = izslēgt
|
shutdown = izslēgt
|
||||||
sleep = snauda
|
sleep = snauda
|
||||||
|
toggle_password = rādīt/slēpt paroli
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
x11 = x11
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -3,23 +3,26 @@ brightness_down = zmniejsz jasność
|
||||||
brightness_up = zwiększ jasność
|
brightness_up = zwiększ jasność
|
||||||
capslock = capslock
|
capslock = capslock
|
||||||
custom = własny
|
custom = własny
|
||||||
|
custom_info_err_output_long = wyjście zbyt długie
|
||||||
|
custom_info_err_no_output = brak wyjścia
|
||||||
|
custom_info_err_no_output_error = , możliwy błąd
|
||||||
err_alloc = nieudana alokacja pamięci
|
err_alloc = nieudana alokacja pamięci
|
||||||
|
err_args = nie można przetworzyć argumentów wiersza poleceń
|
||||||
err_autologin_session = nie znaleziono sesji autologowania
|
err_autologin_session = nie znaleziono sesji autologowania
|
||||||
err_bounds = indeks poza zakresem
|
err_bounds = indeks poza zakresem
|
||||||
err_brightness_change = nie udało się zmienić jasności
|
err_brightness_change = nie udało się zmienić jasności
|
||||||
err_chdir = nie udało się otworzyć folderu domowego
|
err_chdir = nie udało się otworzyć folderu domowego
|
||||||
err_clock_too_long = ciąg znaków zegara jest za długi
|
err_clock_too_long = ciąg znaków zegara jest za długi
|
||||||
err_config = nie można przetworzyć pliku konfiguracyjnego
|
err_config = nie można przetworzyć pliku konfiguracyjnego
|
||||||
|
err_crawl = nie udało się przeszukać katalogów sesji
|
||||||
err_dgn_oob = wiadomość loga
|
err_dgn_oob = wiadomość loga
|
||||||
err_domain = niepoprawna domena
|
err_domain = niepoprawna domena
|
||||||
err_empty_password = puste hasło jest niedozwolone
|
err_empty_password = puste hasło jest niedozwolone
|
||||||
err_envlist = nie udało się pobrać listy zmiennych środowiskowych
|
err_envlist = nie udało się pobrać listy zmiennych środowiskowych
|
||||||
err_get_active_tty = nie udało się uzyskać aktywnego tty
|
err_get_active_tty = nie udało się uzyskać aktywnego tty
|
||||||
|
err_hibernate = nie udało się wykonać polecenia hibernacji
|
||||||
err_hostname = nie udało się uzyskać nazwy hosta
|
err_hostname = nie udało się uzyskać nazwy hosta
|
||||||
|
err_inactivity = nie udało się wykonać polecenia nieaktywności
|
||||||
err_lock_state = nie udało się uzyskać stanu blokady
|
err_lock_state = nie udało się uzyskać stanu blokady
|
||||||
err_log = nie udało się otworzyć pliku logu
|
err_log = nie udało się otworzyć pliku logu
|
||||||
err_mlock = nie udało się zablokować pamięci haseł
|
err_mlock = nie udało się zablokować pamięci haseł
|
||||||
|
|
@ -47,12 +50,12 @@ err_perm_group = nie udało się obniżyć uprawnień grupy
|
||||||
err_perm_user = nie udało się obniżyć uprawnień użytkownika
|
err_perm_user = nie udało się obniżyć uprawnień użytkownika
|
||||||
err_pwnam = nie udało się uzyskać informacji o użytkowniku
|
err_pwnam = nie udało się uzyskać informacji o użytkowniku
|
||||||
err_sleep = nie udało się wykonać polecenia sleep
|
err_sleep = nie udało się wykonać polecenia sleep
|
||||||
|
err_start = nie udało się wykonać polecenia startowego
|
||||||
err_battery = nie udało się sprawdzić statusu baterii
|
err_battery = nie udało się sprawdzić statusu baterii
|
||||||
err_switch_tty = nie można przełączyć tty
|
err_switch_tty = nie można przełączyć tty
|
||||||
err_tty_ctrl = nie udało się przekazać kontroli tty
|
err_tty_ctrl = nie udało się przekazać kontroli tty
|
||||||
err_no_users = nie znaleziono żadnego użytkownika
|
err_no_users = nie znaleziono żadnego użytkownika
|
||||||
|
err_uid_range = nie udało się dynamicznie pobrać zakresu uid
|
||||||
err_user_gid = nie udało się ustawić GID użytkownika
|
err_user_gid = nie udało się ustawić GID użytkownika
|
||||||
err_user_init = nie udało się zainicjalizować użytkownika
|
err_user_init = nie udało się zainicjalizować użytkownika
|
||||||
err_user_uid = nie udało się ustawić UID użytkownika
|
err_user_uid = nie udało się ustawić UID użytkownika
|
||||||
|
|
@ -60,11 +63,11 @@ err_xauth = polecenie xauth nie powiodło się
|
||||||
err_xcb_conn = połączenie xcb nie powiodło się
|
err_xcb_conn = połączenie xcb nie powiodło się
|
||||||
err_xsessions_dir = nie udało się znaleźć folderu sesji
|
err_xsessions_dir = nie udało się znaleźć folderu sesji
|
||||||
err_xsessions_open = nie udało się otworzyć folderu sesji
|
err_xsessions_open = nie udało się otworzyć folderu sesji
|
||||||
|
hibernate = hibernuj
|
||||||
insert = wstaw
|
insert = wstaw
|
||||||
login = login
|
login = login
|
||||||
logout = wylogowano
|
logout = wylogowano
|
||||||
no_x11_support = wsparcie X11 wyłączone podczas kompilacji
|
no_x11_support = obsługa x11 wyłączona podczas kompilacji
|
||||||
normal = normalny
|
normal = normalny
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
other = inny
|
other = inny
|
||||||
|
|
@ -73,6 +76,7 @@ restart = uruchom ponownie
|
||||||
shell = powłoka
|
shell = powłoka
|
||||||
shutdown = wyłącz
|
shutdown = wyłącz
|
||||||
sleep = uśpij
|
sleep = uśpij
|
||||||
|
toggle_password = Pokaż/ukryj hasło
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
x11 = x11
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,33 @@
|
||||||
|
authenticating = a autenticar...
|
||||||
|
brightness_down = diminuir brilho
|
||||||
|
brightness_up = aumentar brilho
|
||||||
capslock = capslock
|
capslock = capslock
|
||||||
|
custom = personalizado
|
||||||
|
custom_info_err_output_long = saída demasiado longa
|
||||||
|
custom_info_err_no_output = sem saída
|
||||||
|
custom_info_err_no_output_error = , possível erro
|
||||||
err_alloc = erro na atribuição de memória
|
err_alloc = erro na atribuição de memória
|
||||||
|
err_args = não foi possível analisar os argumentos da linha de comandos
|
||||||
|
err_autologin_session = sessão de início de sessão automático não encontrada
|
||||||
err_bounds = índice fora de limites
|
err_bounds = índice fora de limites
|
||||||
|
err_brightness_change = não foi possível alterar o brilho
|
||||||
err_chdir = erro ao abrir a pasta home
|
err_chdir = erro ao abrir a pasta home
|
||||||
|
err_clock_too_long = a cadeia de caracteres do relógio é demasiado longa
|
||||||
|
err_config = não foi possível analisar o ficheiro de configuração
|
||||||
|
err_crawl = não foi possível explorar os diretórios de sessão
|
||||||
err_dgn_oob = mensagem de registo
|
err_dgn_oob = mensagem de registo
|
||||||
err_domain = domínio inválido
|
err_domain = domínio inválido
|
||||||
|
err_empty_password = palavra-passe vazia não é permitida
|
||||||
|
err_envlist = não foi possível obter a lista de variáveis de ambiente
|
||||||
|
err_get_active_tty = não foi possível obter o tty ativo
|
||||||
|
err_hibernate = não foi possível executar o comando de hibernação
|
||||||
err_hostname = erro ao obter o nome do host
|
err_hostname = erro ao obter o nome do host
|
||||||
|
err_inactivity = não foi possível executar o comando de inatividade
|
||||||
|
err_lock_state = não foi possível obter o estado de bloqueio
|
||||||
|
err_log = não foi possível abrir o ficheiro de registo
|
||||||
err_mlock = erro de bloqueio de memória
|
err_mlock = erro de bloqueio de memória
|
||||||
err_null = ponteiro nulo
|
err_null = ponteiro nulo
|
||||||
|
err_numlock = não foi possível definir o numlock
|
||||||
err_pam = erro na transação pam
|
err_pam = erro na transação pam
|
||||||
err_pam_abort = transação pam abortada
|
err_pam_abort = transação pam abortada
|
||||||
err_pam_acct_expired = conta expirada
|
err_pam_acct_expired = conta expirada
|
||||||
|
|
@ -46,33 +49,34 @@ err_perm_dir = erro ao alterar o diretório atual
|
||||||
err_perm_group = erro ao reduzir as permissões do grupo
|
err_perm_group = erro ao reduzir as permissões do grupo
|
||||||
err_perm_user = erro ao reduzir as permissões do utilizador
|
err_perm_user = erro ao reduzir as permissões do utilizador
|
||||||
err_pwnam = erro ao obter informação do utilizador
|
err_pwnam = erro ao obter informação do utilizador
|
||||||
|
err_sleep = não foi possível executar o comando de suspensão
|
||||||
|
err_start = não foi possível executar o comando de início
|
||||||
|
err_battery = não foi possível carregar o estado da bateria
|
||||||
|
err_switch_tty = não foi possível mudar de tty
|
||||||
|
err_tty_ctrl = falhou a transferência de controlo do tty
|
||||||
|
err_no_users = nenhum utilizador encontrado
|
||||||
|
err_uid_range = não foi possível obter dinamicamente o intervalo de uid
|
||||||
err_user_gid = erro ao definir o GID do utilizador
|
err_user_gid = erro ao definir o GID do utilizador
|
||||||
err_user_init = erro ao iniciar o utilizador
|
err_user_init = erro ao iniciar o utilizador
|
||||||
err_user_uid = erro ao definir o UID do utilizador
|
err_user_uid = erro ao definir o UID do utilizador
|
||||||
|
err_xauth = o comando xauth falhou
|
||||||
|
err_xcb_conn = a ligação xcb falhou
|
||||||
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
|
||||||
|
hibernate = hibernar
|
||||||
|
insert = inserir
|
||||||
login = iniciar sessão
|
login = iniciar sessão
|
||||||
logout = terminar sessão
|
logout = terminar sessão
|
||||||
|
no_x11_support = suporte a x11 desativado em tempo de compilação
|
||||||
|
normal = normal
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
|
other = outro
|
||||||
password = palavra-passe
|
password = palavra-passe
|
||||||
restart = reiniciar
|
restart = reiniciar
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = encerrar
|
shutdown = encerrar
|
||||||
|
sleep = suspender
|
||||||
|
toggle_password = mostrar/ocultar palavra-passe
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,33 @@
|
||||||
|
authenticating = autenticando...
|
||||||
|
brightness_down = diminuir brilho
|
||||||
|
brightness_up = aumentar brilho
|
||||||
capslock = caixa alta
|
capslock = caixa alta
|
||||||
|
custom = personalizado
|
||||||
|
custom_info_err_output_long = saída muito longa
|
||||||
|
custom_info_err_no_output = sem saída
|
||||||
|
custom_info_err_no_output_error = , possível erro
|
||||||
err_alloc = alocação de memória malsucedida
|
err_alloc = alocação de memória malsucedida
|
||||||
|
err_args = não foi possível analisar os argumentos da linha de comando
|
||||||
|
err_autologin_session = sessão de login automático não encontrada
|
||||||
err_bounds = índice fora de limites
|
err_bounds = índice fora de limites
|
||||||
|
err_brightness_change = não foi possível alterar o brilho
|
||||||
err_chdir = não foi possível abrir o diretório home
|
err_chdir = não foi possível abrir o diretório home
|
||||||
|
err_clock_too_long = a string do relógio é muito longa
|
||||||
|
err_config = não foi possível analisar o arquivo de configuração
|
||||||
|
err_crawl = não foi possível explorar os diretórios de sessão
|
||||||
err_dgn_oob = mensagem de log
|
err_dgn_oob = mensagem de log
|
||||||
err_domain = domínio inválido
|
err_domain = domínio inválido
|
||||||
|
err_empty_password = senha vazia não é permitida
|
||||||
|
err_envlist = não foi possível obter a lista de variáveis de ambiente
|
||||||
|
err_get_active_tty = não foi possível obter o tty ativo
|
||||||
|
err_hibernate = não foi possível executar o comando de hibernação
|
||||||
err_hostname = não foi possível obter o nome do host
|
err_hostname = não foi possível obter o nome do host
|
||||||
|
err_inactivity = não foi possível executar o comando de inatividade
|
||||||
|
err_lock_state = não foi possível obter o estado de bloqueio
|
||||||
|
err_log = não foi possível abrir o arquivo de log
|
||||||
err_mlock = bloqueio da memória de senha malsucedido
|
err_mlock = bloqueio da memória de senha malsucedido
|
||||||
err_null = ponteiro nulo
|
err_null = ponteiro nulo
|
||||||
|
err_numlock = não foi possível definir o numlock
|
||||||
err_pam = transação pam malsucedida
|
err_pam = transação pam malsucedida
|
||||||
err_pam_abort = transação pam abortada
|
err_pam_abort = transação pam abortada
|
||||||
err_pam_acct_expired = conta expirada
|
err_pam_acct_expired = conta expirada
|
||||||
|
|
@ -46,33 +49,34 @@ err_perm_dir = não foi possível alterar o diretório atual
|
||||||
err_perm_group = não foi possível reduzir as permissões de grupo
|
err_perm_group = não foi possível reduzir as permissões de grupo
|
||||||
err_perm_user = não foi possível reduzir as permissões de usuário
|
err_perm_user = não foi possível reduzir as permissões de usuário
|
||||||
err_pwnam = não foi possível obter informações do usuário
|
err_pwnam = não foi possível obter informações do usuário
|
||||||
|
err_sleep = não foi possível executar o comando de suspensão
|
||||||
|
err_start = não foi possível executar o comando de início
|
||||||
|
err_battery = não foi possível carregar o status da bateria
|
||||||
|
err_switch_tty = não foi possível mudar de tty
|
||||||
|
err_tty_ctrl = falhou a transferência de controle do tty
|
||||||
|
err_no_users = nenhum usuário encontrado
|
||||||
|
err_uid_range = não foi possível obter dinamicamente o intervalo de uid
|
||||||
err_user_gid = não foi possível definir o GID do usuário
|
err_user_gid = não foi possível definir o GID do usuário
|
||||||
err_user_init = não foi possível iniciar o usuário
|
err_user_init = não foi possível iniciar o usuário
|
||||||
err_user_uid = não foi possível definir o UID do usuário
|
err_user_uid = não foi possível definir o UID do usuário
|
||||||
|
err_xauth = o comando xauth falhou
|
||||||
|
err_xcb_conn = a conexão xcb falhou
|
||||||
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
|
||||||
|
hibernate = hibernar
|
||||||
|
insert = inserir
|
||||||
login = conectar
|
login = conectar
|
||||||
logout = desconectado
|
logout = desconectado
|
||||||
|
no_x11_support = suporte a x11 desativado em tempo de compilação
|
||||||
|
normal = normal
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
|
other = outro
|
||||||
password = senha
|
password = senha
|
||||||
restart = reiniciar
|
restart = reiniciar
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = desligar
|
shutdown = desligar
|
||||||
|
sleep = suspender
|
||||||
|
toggle_password = mostrar/ocultar senha
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
104
res/lang/ro.ini
104
res/lang/ro.ini
|
|
@ -1,31 +1,34 @@
|
||||||
|
authenticating = autentificare...
|
||||||
|
brightness_down = scade luminozitatea
|
||||||
|
brightness_up = crește luminozitatea
|
||||||
capslock = capslock
|
capslock = capslock
|
||||||
|
custom = personalizat
|
||||||
|
custom_info_err_output_long = ieșire prea lungă
|
||||||
|
custom_info_err_no_output = fără ieșire
|
||||||
|
custom_info_err_no_output_error = , posibil eroare
|
||||||
|
err_alloc = alocare de memorie eșuată
|
||||||
|
err_args = imposibil de analizat argumentele liniei de comandă
|
||||||
|
err_autologin_session = sesiunea de autentificare automată nu a fost găsită
|
||||||
|
err_bounds = index în afara limitelor
|
||||||
|
err_brightness_change = imposibil de schimbat luminozitatea
|
||||||
|
err_chdir = imposibil de deschis folderul de acasă
|
||||||
|
err_clock_too_long = șirul de ceas este prea lung
|
||||||
|
err_config = imposibil de analizat fișierul de configurare
|
||||||
|
err_crawl = imposibil de explorat directoarele de sesiune
|
||||||
|
err_dgn_oob = mesaj jurnal
|
||||||
|
err_domain = domeniu invalid
|
||||||
|
err_empty_password = parola goală nu este permisă
|
||||||
|
err_envlist = imposibil de obținut lista de variabile de mediu
|
||||||
|
err_get_active_tty = imposibil de obținut tty-ul activ
|
||||||
|
err_hibernate = imposibil de executat comanda de hibernare
|
||||||
|
err_hostname = imposibil de obținut numele gazdei
|
||||||
|
err_inactivity = imposibil de executat comanda de inactivitate
|
||||||
|
err_lock_state = imposibil de obținut starea de blocare
|
||||||
|
err_log = imposibil de deschis fișierul jurnal
|
||||||
|
err_mlock = imposibil de blocat memoria parolei
|
||||||
|
err_null = pointer nul
|
||||||
|
err_numlock = imposibil de setat numlock
|
||||||
|
err_pam = tranzacție pam eșuată
|
||||||
err_pam_abort = tranzacţie pam anulată
|
err_pam_abort = tranzacţie pam anulată
|
||||||
err_pam_acct_expired = cont expirat
|
err_pam_acct_expired = cont expirat
|
||||||
err_pam_auth = eroare de autentificare
|
err_pam_auth = eroare de autentificare
|
||||||
|
|
@ -41,38 +44,39 @@ err_pam_perm_denied = acces interzis
|
||||||
err_pam_session = eroare de sesiune
|
err_pam_session = eroare de sesiune
|
||||||
err_pam_sys = eroare de sistem
|
err_pam_sys = eroare de sistem
|
||||||
err_pam_user_unknown = utilizator necunoscut
|
err_pam_user_unknown = utilizator necunoscut
|
||||||
|
err_path = imposibil de setat calea
|
||||||
err_perm_dir = nu s-a putut schimba dosarul (folder-ul) curent
|
err_perm_dir = nu s-a putut schimba dosarul (folder-ul) curent
|
||||||
err_perm_group = nu s-a putut face downgrade permisiunilor de grup
|
err_perm_group = nu s-a putut face downgrade permisiunilor de grup
|
||||||
err_perm_user = nu s-a putut face downgrade permisiunilor de utilizator
|
err_perm_user = nu s-a putut face downgrade permisiunilor de utilizator
|
||||||
|
err_pwnam = imposibil de obținut informații despre utilizator
|
||||||
|
err_sleep = imposibil de executat comanda de repaus
|
||||||
|
err_start = imposibil de executat comanda de pornire
|
||||||
|
err_battery = imposibil de încărcat starea bateriei
|
||||||
|
err_switch_tty = imposibil de comutat tty
|
||||||
|
err_tty_ctrl = transferul controlului tty a eșuat
|
||||||
|
err_no_users = niciun utilizator găsit
|
||||||
|
err_uid_range = imposibil de obținut dinamic intervalul uid
|
||||||
|
err_user_gid = imposibil de setat GID-ul utilizatorului
|
||||||
|
err_user_init = imposibil de inițializa utilizatorul
|
||||||
|
err_user_uid = imposibil de setat UID-ul utilizatorului
|
||||||
|
err_xauth = comanda xauth a eșuat
|
||||||
|
err_xcb_conn = conexiunea xcb a eșuat
|
||||||
|
err_xsessions_dir = imposibil de găsit folderul de sesiuni
|
||||||
|
err_xsessions_open = imposibil de deschis folderul de sesiuni
|
||||||
|
hibernate = hibernare
|
||||||
|
insert = inserare
|
||||||
login = utilizator
|
login = utilizator
|
||||||
logout = opreşte sesiunea
|
logout = opreşte sesiunea
|
||||||
|
no_x11_support = suportul x11 dezactivat la compilare
|
||||||
|
normal = normal
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
|
other = altul
|
||||||
password = parolă
|
password = parolă
|
||||||
restart = resetează
|
restart = resetează
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = opreşte sistemul
|
shutdown = opreşte sistemul
|
||||||
|
sleep = repaus
|
||||||
|
toggle_password = afișare/ascundere parolă
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -3,23 +3,26 @@ brightness_down = уменьшить яркость
|
||||||
brightness_up = увеличить яркость
|
brightness_up = увеличить яркость
|
||||||
capslock = capslock
|
capslock = capslock
|
||||||
custom = пользовательский
|
custom = пользовательский
|
||||||
|
custom_info_err_output_long = вывод слишком длинный
|
||||||
|
custom_info_err_no_output = нет вывода
|
||||||
|
custom_info_err_no_output_error = , возможная ошибка
|
||||||
err_alloc = не удалось выделить память
|
err_alloc = не удалось выделить память
|
||||||
|
err_args = не удалось разобрать аргументы командной строки
|
||||||
err_autologin_session = не найдена сессия с автологином
|
err_autologin_session = не найдена сессия с автологином
|
||||||
err_bounds = за пределами индекса
|
err_bounds = за пределами индекса
|
||||||
err_brightness_change = не удалось изменить яркость
|
err_brightness_change = не удалось изменить яркость
|
||||||
err_chdir = не удалось открыть домашнюю папку
|
err_chdir = не удалось открыть домашнюю папку
|
||||||
err_clock_too_long = строка часов слишком длинная
|
err_clock_too_long = строка часов слишком длинная
|
||||||
err_config = не удалось разобрать файл конфигурации
|
err_config = не удалось разобрать файл конфигурации
|
||||||
|
err_crawl = не удалось просканировать каталоги сессии
|
||||||
err_dgn_oob = отладочное сообщение (log)
|
err_dgn_oob = отладочное сообщение (log)
|
||||||
err_domain = неверный домен
|
err_domain = неверный домен
|
||||||
err_empty_password = пустой пароль не допустим
|
err_empty_password = пустой пароль не допустим
|
||||||
err_envlist = не удалось получить список переменных среды
|
err_envlist = не удалось получить список переменных среды
|
||||||
err_get_active_tty = не удалось получить активный tty
|
err_get_active_tty = не удалось получить активный tty
|
||||||
|
err_hibernate = не удалось выполнить команду гибернации
|
||||||
err_hostname = не удалось получить имя хоста
|
err_hostname = не удалось получить имя хоста
|
||||||
|
err_inactivity = не удалось выполнить команду бездействия
|
||||||
err_lock_state = не удалось получить состояние lock
|
err_lock_state = не удалось получить состояние lock
|
||||||
err_log = не удалось открыть файл log
|
err_log = не удалось открыть файл log
|
||||||
err_mlock = сбой блокировки памяти
|
err_mlock = сбой блокировки памяти
|
||||||
|
|
@ -47,12 +50,12 @@ err_perm_group = не удалось понизить права доступа
|
||||||
err_perm_user = не удалось понизить права доступа пользователя
|
err_perm_user = не удалось понизить права доступа пользователя
|
||||||
err_pwnam = не удалось получить информацию о пользователе
|
err_pwnam = не удалось получить информацию о пользователе
|
||||||
err_sleep = не удалось выполнить команду sleep
|
err_sleep = не удалось выполнить команду sleep
|
||||||
|
err_start = не удалось выполнить команду запуска
|
||||||
err_battery = не удалось получить статус батареи
|
err_battery = не удалось получить статус батареи
|
||||||
err_switch_tty = не удалось переключить tty
|
err_switch_tty = не удалось переключить tty
|
||||||
err_tty_ctrl = передача управления tty не удалась
|
err_tty_ctrl = передача управления tty не удалась
|
||||||
err_no_users = пользователи не найдены
|
err_no_users = пользователи не найдены
|
||||||
|
err_uid_range = не удалось динамически получить диапазон uid
|
||||||
err_user_gid = не удалось установить GID пользователя
|
err_user_gid = не удалось установить GID пользователя
|
||||||
err_user_init = не удалось инициализировать пользователя
|
err_user_init = не удалось инициализировать пользователя
|
||||||
err_user_uid = не удалось установить UID пользователя
|
err_user_uid = не удалось установить UID пользователя
|
||||||
|
|
@ -60,11 +63,11 @@ err_xauth = команда xauth не выполнена
|
||||||
err_xcb_conn = ошибка подключения xcb
|
err_xcb_conn = ошибка подключения xcb
|
||||||
err_xsessions_dir = не удалось найти сессионную папку
|
err_xsessions_dir = не удалось найти сессионную папку
|
||||||
err_xsessions_open = не удалось открыть сессионную папку
|
err_xsessions_open = не удалось открыть сессионную папку
|
||||||
|
hibernate = гибернация
|
||||||
insert = вставка
|
insert = вставка
|
||||||
login = логин
|
login = логин
|
||||||
logout = вышел из системы
|
logout = вышел из системы
|
||||||
no_x11_support = поддержка x11 отключена во время компиляции
|
no_x11_support = поддержка x11 отключена при компиляции
|
||||||
normal = обычный
|
normal = обычный
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
other = прочие
|
other = прочие
|
||||||
|
|
@ -73,6 +76,7 @@ restart = перезагрузить
|
||||||
shell = оболочка
|
shell = оболочка
|
||||||
shutdown = выключить
|
shutdown = выключить
|
||||||
sleep = сон
|
sleep = сон
|
||||||
|
toggle_password = показать/скрыть пароль
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
x11 = x11
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
124
res/lang/sr.ini
124
res/lang/sr.ini
|
|
@ -1,78 +1,82 @@
|
||||||
|
authenticating = autentifikacija...
|
||||||
|
brightness_down = smanji osvetljenost
|
||||||
|
brightness_up = povećaj osvetljenost
|
||||||
capslock = capslock
|
capslock = capslock
|
||||||
|
custom = prilagođeno
|
||||||
err_alloc = neuspijesna alokacija memorije
|
custom_info_err_output_long = izlaz predugačak
|
||||||
|
custom_info_err_no_output = nema izlaza
|
||||||
|
custom_info_err_no_output_error = , moguća greška
|
||||||
|
err_alloc = neuspješna alokacija memorije
|
||||||
|
err_args = nije moguće raščlaniti argumente komandne linije
|
||||||
|
err_autologin_session = sesija automatske prijave nije pronađena
|
||||||
err_bounds = izvan granica indeksa
|
err_bounds = izvan granica indeksa
|
||||||
|
err_brightness_change = nije uspela promena osvetljenosti
|
||||||
err_chdir = neuspijesno otvaranje home foldera
|
err_chdir = neuspješno otvaranje home foldera
|
||||||
|
err_clock_too_long = niska sata je preduga
|
||||||
|
err_config = nije moguće raščlaniti konfiguracioni fajl
|
||||||
|
err_crawl = nije uspelo skeniranje direktorijuma sesija
|
||||||
err_dgn_oob = log poruka
|
err_dgn_oob = log poruka
|
||||||
err_domain = nevazeci domen
|
err_domain = nevažeći domen
|
||||||
|
err_empty_password = prazna lozinka nije dozvoljena
|
||||||
|
err_envlist = nije uspelo dobijanje liste promenljivih okruženja
|
||||||
|
err_get_active_tty = nije uspelo dobijanje aktivnog tty
|
||||||
|
err_hibernate = nije uspelo izvršavanje komande hibernacije
|
||||||
err_hostname = neuspijesno trazenje hostname-a
|
err_hostname = neuspješno traženje hostname-a
|
||||||
|
err_inactivity = nije uspelo izvršavanje komande neaktivnosti
|
||||||
|
err_lock_state = nije uspelo dobijanje stanja zaključavanja
|
||||||
|
err_log = nije uspelo otvaranje fajla dnevnika
|
||||||
err_mlock = neuspijesno zakljucavanje memorije lozinke
|
err_mlock = neuspješno zaključavanje memorije lozinke
|
||||||
err_null = null pokazivac
|
err_null = null pokazivač
|
||||||
|
err_numlock = nije uspelo postavljanje numlock
|
||||||
err_pam = pam transakcija neuspijesna
|
err_pam = pam transakcija neuspješna
|
||||||
err_pam_abort = pam transakcija prekinuta
|
err_pam_abort = pam transakcija prekinuta
|
||||||
err_pam_acct_expired = nalog istekao
|
err_pam_acct_expired = nalog istekao
|
||||||
err_pam_auth = greska pri autentikaciji
|
err_pam_auth = greška pri autentikaciji
|
||||||
err_pam_authinfo_unavail = neuspjelo uzimanje informacija o korisniku
|
err_pam_authinfo_unavail = neuspješno uzimanje informacija o korisniku
|
||||||
err_pam_authok_reqd = token istekao
|
err_pam_authok_reqd = token istekao
|
||||||
err_pam_buf = greska bafera memorije
|
err_pam_buf = greška bafera memorije
|
||||||
err_pam_cred_err = neuspjelo postavljanje kredencijala
|
err_pam_cred_err = neuspješno postavljanje kredencijala
|
||||||
err_pam_cred_expired = kredencijali istekli
|
err_pam_cred_expired = kredencijali istekli
|
||||||
err_pam_cred_insufficient = nedovoljni kredencijali
|
err_pam_cred_insufficient = nedovoljni kredencijali
|
||||||
err_pam_cred_unavail = neuspjelo uzimanje kredencijala
|
err_pam_cred_unavail = neuspješno uzimanje kredencijala
|
||||||
err_pam_maxtries = dostignut maksimalan broj pokusaja
|
err_pam_maxtries = dostignut maksimalan broj pokušaja
|
||||||
err_pam_perm_denied = nedozovoljeno
|
err_pam_perm_denied = nedozvoljeno
|
||||||
err_pam_session = greska sesije
|
err_pam_session = greška sesije
|
||||||
err_pam_sys = greska sistema
|
err_pam_sys = greška sistema
|
||||||
err_pam_user_unknown = nepoznat korisnik
|
err_pam_user_unknown = nepoznat korisnik
|
||||||
err_path = neuspjelo postavljanje path-a
|
err_path = neuspješno postavljanje path-a
|
||||||
err_perm_dir = neuspjelo mijenjanje foldera
|
err_perm_dir = neuspješno mijenjanje foldera
|
||||||
err_perm_group = neuspjesno snizavanje dozvola grupe
|
err_perm_group = neuspješno snižavanje dozvola grupe
|
||||||
err_perm_user = neuspijesno snizavanje dozvola korisnika
|
err_perm_user = neuspješno snižavanje dozvola korisnika
|
||||||
err_pwnam = neuspijesno skupljanje informacija o korisniku
|
err_pwnam = neuspješno skupljanje informacija o korisniku
|
||||||
|
err_sleep = nije uspelo izvršavanje komande spavanja
|
||||||
|
err_start = nije uspelo izvršavanje komande pokretanja
|
||||||
|
err_battery = nije uspelo učitavanje statusa baterije
|
||||||
|
err_switch_tty = nije uspelo prebacivanje tty
|
||||||
|
err_tty_ctrl = prenos kontrole tty nije uspeo
|
||||||
|
err_no_users = nisu pronađeni korisnici
|
||||||
|
err_uid_range = nije uspelo dinamičko dobijanje opsega uid
|
||||||
err_user_gid = neuspijesno postavljanje korisničkog GID-a
|
err_user_gid = neuspješno postavljanje korisničkog GID-a
|
||||||
err_user_init = neuspijensa inicijalizacija korisnika
|
err_user_init = neuspješna inicijalizacija korisnika
|
||||||
err_user_uid = neuspijesno postavljanje UID-a korisnika
|
err_user_uid = neuspješno postavljanje UID-a korisnika
|
||||||
|
err_xauth = komanda xauth nije uspela
|
||||||
|
err_xcb_conn = xcb veza nije uspela
|
||||||
err_xsessions_dir = neuspijesno pronalazenje foldera sesija
|
err_xsessions_dir = neuspješno pronalaženje foldera sesija
|
||||||
err_xsessions_open = neuspijesno otvaranje foldera sesija
|
err_xsessions_open = neuspješno otvaranje foldera sesija
|
||||||
|
hibernate = hibernacija
|
||||||
|
insert = umetni
|
||||||
login = korisnik
|
login = korisnik
|
||||||
logout = izlogovan
|
logout = izlogovan
|
||||||
|
no_x11_support = x11 podrška onemogućena tokom prevođenja
|
||||||
|
normal = normalno
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
|
other = ostalo
|
||||||
password = lozinka
|
password = lozinka
|
||||||
restart = ponovo pokreni
|
restart = ponovo pokreni
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = ugasi
|
shutdown = ugasi
|
||||||
|
sleep = uspavaj
|
||||||
|
toggle_password = prikaži/sakrij lozinku
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
authenticating = аутентификација...
|
||||||
|
brightness_down = смањи осветљеност
|
||||||
|
brightness_up = повећај осветљеност
|
||||||
|
capslock = capslock
|
||||||
|
custom = прилагођено
|
||||||
|
custom_info_err_output_long = излаз предугачак
|
||||||
|
custom_info_err_no_output = нема излаза
|
||||||
|
custom_info_err_no_output_error = , могућа грешка
|
||||||
|
err_alloc = неуспешна алокација меморије
|
||||||
|
err_args = није могуће рашчланити аргументе командне линије
|
||||||
|
err_autologin_session = сесија аутоматске пријаве није пронађена
|
||||||
|
err_bounds = изван граница индекса
|
||||||
|
err_brightness_change = није успела промена осветљености
|
||||||
|
err_chdir = неуспешно отварање home фолдера
|
||||||
|
err_clock_too_long = ниска сата је предуга
|
||||||
|
err_config = није могуће рашчланити конфигурациони фајл
|
||||||
|
err_crawl = није успело скенирање директоријума сесија
|
||||||
|
err_dgn_oob = лог порука
|
||||||
|
err_domain = неважећи домен
|
||||||
|
err_empty_password = празна лозинка није дозвољена
|
||||||
|
err_envlist = није успело добијање листе променљивих окружења
|
||||||
|
err_get_active_tty = није успело добијање активног tty
|
||||||
|
err_hibernate = није успело извршавање команде хибернације
|
||||||
|
err_hostname = неуспешно тражење hostname-а
|
||||||
|
err_inactivity = није успело извршавање команде неактивности
|
||||||
|
err_lock_state = није успело добијање стања закључавања
|
||||||
|
err_log = није успело отварање фајла дневника
|
||||||
|
err_mlock = неуспешно закључавање меморије лозинке
|
||||||
|
err_null = null показивач
|
||||||
|
err_numlock = није успело постављање numlock
|
||||||
|
err_pam = pam трансакција неуспешна
|
||||||
|
err_pam_abort = pam трансакција прекинута
|
||||||
|
err_pam_acct_expired = налог истекао
|
||||||
|
err_pam_auth = грешка при аутентикацији
|
||||||
|
err_pam_authinfo_unavail = неуспешно узимање информација о кориснику
|
||||||
|
err_pam_authok_reqd = токен истекао
|
||||||
|
err_pam_buf = грешка бафера меморије
|
||||||
|
err_pam_cred_err = неуспешно постављање акредитива
|
||||||
|
err_pam_cred_expired = акредитиви истекли
|
||||||
|
err_pam_cred_insufficient = недовољни акредитиви
|
||||||
|
err_pam_cred_unavail = неуспешно узимање акредитива
|
||||||
|
err_pam_maxtries = достигнут максималан број покушаја
|
||||||
|
err_pam_perm_denied = недозвољено
|
||||||
|
err_pam_session = грешка сесије
|
||||||
|
err_pam_sys = грешка система
|
||||||
|
err_pam_user_unknown = непознат корисник
|
||||||
|
err_path = неуспешно постављање путање
|
||||||
|
err_perm_dir = неуспешно мењање фолдера
|
||||||
|
err_perm_group = неуспешно снижавање дозвола групе
|
||||||
|
err_perm_user = неуспешно снижавање дозвола корисника
|
||||||
|
err_pwnam = неуспешно прикупљање информација о кориснику
|
||||||
|
err_sleep = није успело извршавање команде спавања
|
||||||
|
err_start = није успело извршавање команде покретања
|
||||||
|
err_battery = није успело учитавање статуса батерије
|
||||||
|
err_switch_tty = није успело пребацивање tty
|
||||||
|
err_tty_ctrl = пренос контроле tty није успео
|
||||||
|
err_no_users = нису пронађени корисници
|
||||||
|
err_uid_range = није успело динамичко добијање опсега uid
|
||||||
|
err_user_gid = неуспешно постављање корисничког GID-а
|
||||||
|
err_user_init = неуспешна иницијализација корисника
|
||||||
|
err_user_uid = неуспешно постављање UID-а корисника
|
||||||
|
err_xauth = команда xauth није успела
|
||||||
|
err_xcb_conn = xcb веза није успела
|
||||||
|
err_xsessions_dir = неуспешно проналажење фолдера сесија
|
||||||
|
err_xsessions_open = неуспешно отварање фолдера сесија
|
||||||
|
hibernate = хибернација
|
||||||
|
insert = уметни
|
||||||
|
login = корисник
|
||||||
|
logout = одјављен
|
||||||
|
no_x11_support = x11 подршка онемогућена током превођења
|
||||||
|
normal = нормално
|
||||||
|
numlock = numlock
|
||||||
|
other = остало
|
||||||
|
password = лозинка
|
||||||
|
restart = поново покрени
|
||||||
|
shell = shell
|
||||||
|
shutdown = угаси
|
||||||
|
sleep = успавај
|
||||||
|
toggle_password = прикажи/сакриј лозинку
|
||||||
|
wayland = wayland
|
||||||
|
x11 = x11
|
||||||
|
xinitrc = xinitrc
|
||||||
124
res/lang/sv.ini
124
res/lang/sv.ini
|
|
@ -1,78 +1,82 @@
|
||||||
|
authenticating = autentiserar...
|
||||||
|
brightness_down = minska ljusstyrka
|
||||||
|
brightness_up = öka ljusstyrka
|
||||||
capslock = capslock
|
capslock = capslock
|
||||||
|
custom = anpassad
|
||||||
err_alloc = misslyckad minnesallokering
|
custom_info_err_output_long = utdata för lång
|
||||||
|
custom_info_err_no_output = ingen utdata
|
||||||
|
custom_info_err_no_output_error = , möjligt fel
|
||||||
err_bounds = utanför banan index
|
err_alloc = minnesallokering misslyckades
|
||||||
|
err_args = tolkning av kommandoargument misslyckades
|
||||||
|
err_autologin_session = autologin-session hittades inte
|
||||||
|
err_bounds = index-värde utanför intervallet
|
||||||
|
err_brightness_change = ändring av ljusstyrka misslyckades
|
||||||
err_chdir = misslyckades att öppna hemkatalog
|
err_chdir = misslyckades att öppna hemkatalog
|
||||||
|
err_clock_too_long = klocksträng för lång
|
||||||
|
err_config = tolkning av konfigfil misslyckades
|
||||||
|
err_crawl = genomsökning av sessionskataloger misslyckades
|
||||||
err_dgn_oob = loggmeddelande
|
err_dgn_oob = loggmeddelande
|
||||||
err_domain = okänd domän
|
err_domain = ogitlig domän
|
||||||
|
err_empty_password = tomt lösenord godtas ej
|
||||||
|
err_envlist = hämtning av env-lista misslyckades
|
||||||
|
err_get_active_tty = hämtning av aktiv tty misslyckades
|
||||||
|
err_hibernate = vilolägets kommando misslyckades
|
||||||
err_hostname = misslyckades att hämta värdnamn
|
err_hostname = hämtning av hostname misslyckades
|
||||||
|
err_inactivity = inaktivitetslägets kommando misslyckades
|
||||||
|
err_lock_state = hämtning av låsningsstatus misslyckades
|
||||||
|
err_log = öppning av loggfil misslyckades
|
||||||
err_mlock = misslyckades att låsa lösenordsminne
|
err_mlock = låsning av lösenordsminne misslyckades
|
||||||
err_null = nullpekare
|
err_null = nullpekare
|
||||||
|
err_numlock = inställning av numlock misslyckades
|
||||||
err_pam = pam-transaktion misslyckades
|
err_pam = pam-transaktion misslyckades
|
||||||
err_pam_abort = pam-transaktion avbröts
|
err_pam_abort = pam-transaktion avbröts
|
||||||
err_pam_acct_expired = konto upphört
|
err_pam_acct_expired = kontot har löpt ut
|
||||||
err_pam_auth = autentiseringsfel
|
err_pam_auth = autentisering misslyckades
|
||||||
err_pam_authinfo_unavail = misslyckades att hämta användarinfo
|
err_pam_authinfo_unavail = hämtning av användarinformation misslyckades
|
||||||
err_pam_authok_reqd = token utgången
|
err_pam_authok_reqd = token har löpt ut
|
||||||
err_pam_buf = minnesbuffer fel
|
err_pam_buf = minnesbufferfel
|
||||||
err_pam_cred_err = misslyckades att ställa in inloggningsuppgifter
|
err_pam_cred_err = inställning av inloggningsuppgifter misslyckades
|
||||||
err_pam_cred_expired = inloggningsuppgifter upphörda
|
err_pam_cred_expired = inloggningsuppgifterna har löpt ut
|
||||||
err_pam_cred_insufficient = otillräckliga inloggningsuppgifter
|
err_pam_cred_insufficient = otillräckliga inloggningsuppgifter
|
||||||
err_pam_cred_unavail = misslyckades att hämta inloggningsuppgifter
|
err_pam_cred_unavail = hämtning av inloggningsuppgifter misslyckades
|
||||||
err_pam_maxtries = nådde maximal försöksgräns
|
err_pam_maxtries = gränsen för antal försök nådd
|
||||||
err_pam_perm_denied = åtkomst nekad
|
err_pam_perm_denied = tillstånd nekas
|
||||||
err_pam_session = sessionsfel
|
err_pam_session = sessionsfel
|
||||||
err_pam_sys = systemfel
|
err_pam_sys = systemfel
|
||||||
err_pam_user_unknown = okänd användare
|
err_pam_user_unknown = okänd användare
|
||||||
err_path = misslyckades att ställa in sökväg
|
err_path = inställning av sökväg misslyckades
|
||||||
err_perm_dir = misslyckades att ändra aktuell katalog
|
err_perm_dir = byte av nuvarande katalog misslyckades
|
||||||
err_perm_group = misslyckades att nergradera gruppbehörigheter
|
err_perm_group = nedgradering av grupptillstånd misslyckades
|
||||||
err_perm_user = misslyckades att nergradera användarbehörigheter
|
err_perm_user = nedgradering av användartillstånd misslyckades
|
||||||
err_pwnam = misslyckades att hämta användarinfo
|
err_pwnam = hämtning av användarinformation misslyckades
|
||||||
|
err_sleep = strömsparlägets kommando misslyckades
|
||||||
|
err_start = startkommando misslyckades
|
||||||
|
err_battery = hämtning av batteristatus misslyckades
|
||||||
|
err_switch_tty = byte av tty misslyckades
|
||||||
|
err_tty_ctrl = överföring av tty-kontroll misslyckades
|
||||||
|
err_no_users = inga användare hittades
|
||||||
|
err_uid_range = dynamisk hämtning av uid-intervall misslyckades
|
||||||
err_user_gid = misslyckades att ställa in användar-GID
|
err_user_gid = inställning av användarens GID misslyckades
|
||||||
err_user_init = misslyckades att initialisera användaren
|
err_user_init = initiering av användare misslyckades
|
||||||
err_user_uid = misslyckades att ställa in användar-UID
|
err_user_uid = inställning av användarens UID misslyckades
|
||||||
|
err_xauth = xauth-kommando misslyckades
|
||||||
|
err_xcb_conn = xcb-anslutning misslyckades
|
||||||
err_xsessions_dir = misslyckades att hitta sessionskatalog
|
err_xsessions_dir = sessionskatalog hittades inte
|
||||||
err_xsessions_open = misslyckades att öppna sessionskatalog
|
err_xsessions_open = öppning av sessionskatalog misslyckades
|
||||||
|
hibernate = viloläge
|
||||||
|
insert = infoga
|
||||||
login = inloggning
|
login = inloggning
|
||||||
logout = utloggad
|
logout = utloggad
|
||||||
|
no_x11_support = x11-stöd inaktiverat vid kompilering
|
||||||
|
normal = normal
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
|
other = övrig
|
||||||
password = lösenord
|
password = lösenord
|
||||||
restart = starta om
|
restart = starta om
|
||||||
shell = skal
|
shell = shell
|
||||||
shutdown = stäng av
|
shutdown = stäng av
|
||||||
|
sleep = viloläge
|
||||||
|
toggle_password = visa/dölj lösenord
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,33 @@
|
||||||
|
authenticating = kimlik doğrulanıyor...
|
||||||
|
brightness_down = parlakligi azalt
|
||||||
|
brightness_up = parlakligi arttir
|
||||||
capslock = capslock
|
capslock = capslock
|
||||||
|
custom = özel
|
||||||
|
custom_info_err_output_long = çıktı çok uzun
|
||||||
|
custom_info_err_no_output = çıktı yok
|
||||||
|
custom_info_err_no_output_error = , olası hata
|
||||||
err_alloc = basarisiz bellek ayirma
|
err_alloc = basarisiz bellek ayirma
|
||||||
|
err_args = komut satırı argümanları ayrıştırılamıyor
|
||||||
|
err_autologin_session = otomatik oturum açma oturumu bulunamadı
|
||||||
err_bounds = sinirlarin disinda dizin
|
err_bounds = sinirlarin disinda dizin
|
||||||
|
err_brightness_change = parlaklık değiştirilemedi
|
||||||
err_chdir = ev klasoru acilamadi
|
err_chdir = ev klasoru acilamadi
|
||||||
|
err_clock_too_long = saat dizesi çok uzun
|
||||||
|
err_config = yapılandırma dosyası ayrıştırılamıyor
|
||||||
|
err_crawl = oturum dizinleri taranamadı
|
||||||
err_dgn_oob = log mesaji
|
err_dgn_oob = log mesaji
|
||||||
err_domain = gecersiz etki alani
|
err_domain = gecersiz etki alani
|
||||||
|
err_empty_password = boş parola kullanılamaz
|
||||||
|
err_envlist = ortam değişkenleri listesi alınamadı
|
||||||
|
err_get_active_tty = aktif tty alınamadı
|
||||||
|
err_hibernate = hazırda bekletme komutu çalıştırılamadı
|
||||||
err_hostname = ana bilgisayar adi alinamadi
|
err_hostname = ana bilgisayar adi alinamadi
|
||||||
|
err_inactivity = hareketsizlik komutu çalıştırılamadı
|
||||||
|
err_lock_state = kilit durumu alınamadı
|
||||||
|
err_log = günlük dosyası açılamadı
|
||||||
err_mlock = parola bellegi kilitlenemedi
|
err_mlock = parola bellegi kilitlenemedi
|
||||||
err_null = bos isaretci hatasi
|
err_null = bos isaretci hatasi
|
||||||
|
err_numlock = numlock ayarlanamadı
|
||||||
err_pam = pam islemi basarisiz oldu
|
err_pam = pam islemi basarisiz oldu
|
||||||
err_pam_abort = pam islemi durduruldu
|
err_pam_abort = pam islemi durduruldu
|
||||||
err_pam_acct_expired = hesabin suresi dolmus
|
err_pam_acct_expired = hesabin suresi dolmus
|
||||||
|
|
@ -46,33 +49,34 @@ err_perm_dir = gecerli dizin degistirilemedi
|
||||||
err_perm_group = grup izinleri dusurulemedi
|
err_perm_group = grup izinleri dusurulemedi
|
||||||
err_perm_user = kullanici izinleri dusurulemedi
|
err_perm_user = kullanici izinleri dusurulemedi
|
||||||
err_pwnam = kullanici bilgileri alinamadi
|
err_pwnam = kullanici bilgileri alinamadi
|
||||||
|
err_sleep = uyku komutu çalıştırılamadı
|
||||||
|
err_start = başlatma komutu çalıştırılamadı
|
||||||
|
err_battery = pil durumu yüklenemedi
|
||||||
|
err_switch_tty = tty değiştirilemedi
|
||||||
|
err_tty_ctrl = tty kontrol aktarımı başarısız oldu
|
||||||
|
err_no_users = kullanıcı bulunamadı
|
||||||
|
err_uid_range = uid aralığı dinamik olarak alınamadı
|
||||||
err_user_gid = kullanici icin GID ayarlanamadi
|
err_user_gid = kullanici icin GID ayarlanamadi
|
||||||
err_user_init = kullanici oturumu baslatilamadi
|
err_user_init = kullanici oturumu baslatilamadi
|
||||||
err_user_uid = kullanici icin UID ayarlanamadi
|
err_user_uid = kullanici icin UID ayarlanamadi
|
||||||
|
err_xauth = xauth komutu başarısız oldu
|
||||||
|
err_xcb_conn = xcb bağlantısı başarısız oldu
|
||||||
err_xsessions_dir = oturumlar klasoru bulunamadi
|
err_xsessions_dir = oturumlar klasoru bulunamadi
|
||||||
err_xsessions_open = oturumlar klasoru acilamadi
|
err_xsessions_open = oturumlar klasoru acilamadi
|
||||||
|
hibernate = askiya al
|
||||||
|
insert = ekle
|
||||||
login = kullanici
|
login = kullanici
|
||||||
logout = oturumdan cikis yapildi
|
logout = oturumdan cikis yapildi
|
||||||
|
no_x11_support = x11 desteği derleme zamanında devre dışı bırakıldı
|
||||||
|
normal = normal
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
|
other = baska
|
||||||
password = sifre
|
password = sifre
|
||||||
restart = yeniden baslat
|
restart = yeniden baslat
|
||||||
shell = shell
|
shell = shell
|
||||||
shutdown = makineyi kapat
|
shutdown = makineyi kapat
|
||||||
|
sleep = uykuya al
|
||||||
|
toggle_password = parolayı göster/gizle
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,33 @@
|
||||||
|
authenticating = автентифікація...
|
||||||
|
brightness_down = зменшити яскравість
|
||||||
|
brightness_up = збільшити яскравість
|
||||||
capslock = capslock
|
capslock = capslock
|
||||||
|
custom = власний
|
||||||
|
custom_info_err_output_long = вивід занадто довгий
|
||||||
|
custom_info_err_no_output = немає виводу
|
||||||
|
custom_info_err_no_output_error = , можлива помилка
|
||||||
err_alloc = невдале виділення пам'яті
|
err_alloc = невдале виділення пам'яті
|
||||||
|
err_args = не вдалося розібрати аргументи командного рядка
|
||||||
|
err_autologin_session = сеанс автоматичного входу не знайдено
|
||||||
err_bounds = поза межами індексу
|
err_bounds = поза межами індексу
|
||||||
|
err_brightness_change = не вдалося змінити яскравість
|
||||||
err_chdir = не вдалося відкрити домашній каталог
|
err_chdir = не вдалося відкрити домашній каталог
|
||||||
|
err_clock_too_long = рядок годинника занадто довгий
|
||||||
|
err_config = не вдалося розібрати файл конфігурації
|
||||||
|
err_crawl = не вдалося сканувати каталоги сесій
|
||||||
err_dgn_oob = повідомлення журналу (log)
|
err_dgn_oob = повідомлення журналу (log)
|
||||||
err_domain = недійсний домен
|
err_domain = недійсний домен
|
||||||
|
err_empty_password = порожній пароль не дозволено
|
||||||
|
err_envlist = не вдалося отримати список змінних середовища
|
||||||
|
err_get_active_tty = не вдалося отримати активний tty
|
||||||
|
err_hibernate = не вдалося виконати команду гібернації
|
||||||
err_hostname = не вдалося отримати ім'я хосту
|
err_hostname = не вдалося отримати ім'я хосту
|
||||||
|
err_inactivity = не вдалося виконати команду неактивності
|
||||||
|
err_lock_state = не вдалося отримати стан блокування
|
||||||
|
err_log = не вдалося відкрити файл журналу
|
||||||
err_mlock = збій блокування пам'яті
|
err_mlock = збій блокування пам'яті
|
||||||
err_null = нульовий вказівник
|
err_null = нульовий вказівник
|
||||||
|
err_numlock = не вдалося встановити numlock
|
||||||
err_pam = невдала pam транзакція
|
err_pam = невдала pam транзакція
|
||||||
err_pam_abort = pam транзакція перервана
|
err_pam_abort = pam транзакція перервана
|
||||||
err_pam_acct_expired = термін дії акаунту вичерпано
|
err_pam_acct_expired = термін дії акаунту вичерпано
|
||||||
|
|
@ -46,33 +49,34 @@ err_perm_dir = не вдалося змінити поточний катало
|
||||||
err_perm_group = не вдалося понизити права доступу групи
|
err_perm_group = не вдалося понизити права доступу групи
|
||||||
err_perm_user = не вдалося понизити права доступу користувача
|
err_perm_user = не вдалося понизити права доступу користувача
|
||||||
err_pwnam = не вдалося отримати дані користувача
|
err_pwnam = не вдалося отримати дані користувача
|
||||||
|
err_sleep = не вдалося виконати команду сну
|
||||||
|
err_start = не вдалося виконати команду запуску
|
||||||
|
err_battery = не вдалося завантажити стан акумулятора
|
||||||
|
err_switch_tty = не вдалося переключити tty
|
||||||
|
err_tty_ctrl = передача керування tty не вдалася
|
||||||
|
err_no_users = користувачів не знайдено
|
||||||
|
err_uid_range = не вдалося динамічно отримати діапазон uid
|
||||||
err_user_gid = не вдалося змінити GID користувача
|
err_user_gid = не вдалося змінити GID користувача
|
||||||
err_user_init = не вдалося ініціалізувати користувача
|
err_user_init = не вдалося ініціалізувати користувача
|
||||||
err_user_uid = не вдалося змінити UID користувача
|
err_user_uid = не вдалося змінити UID користувача
|
||||||
|
err_xauth = команда xauth не вдалася
|
||||||
|
err_xcb_conn = з'єднання xcb не вдалося
|
||||||
err_xsessions_dir = не вдалося знайти каталог сесій
|
err_xsessions_dir = не вдалося знайти каталог сесій
|
||||||
err_xsessions_open = не вдалося відкрити каталог сесій
|
err_xsessions_open = не вдалося відкрити каталог сесій
|
||||||
|
hibernate = гібернація
|
||||||
|
insert = вставити
|
||||||
login = логін
|
login = логін
|
||||||
logout = вийти
|
logout = вийти
|
||||||
|
no_x11_support = підтримку x11 вимкнено під час компіляції
|
||||||
|
normal = нормальний
|
||||||
numlock = numlock
|
numlock = numlock
|
||||||
|
other = інший
|
||||||
password = пароль
|
password = пароль
|
||||||
restart = перезавантажити
|
restart = перезавантажити
|
||||||
shell = оболонка
|
shell = оболонка
|
||||||
shutdown = вимкнути
|
shutdown = вимкнути
|
||||||
|
sleep = сплячий режим
|
||||||
|
toggle_password = показати/приховати пароль
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,33 @@
|
||||||
|
authenticating = 正在认证...
|
||||||
|
brightness_down = 降低亮度
|
||||||
|
brightness_up = 提高亮度
|
||||||
capslock = 大写锁定
|
capslock = 大写锁定
|
||||||
|
custom = 自定义
|
||||||
|
custom_info_err_output_long = 输出过长
|
||||||
|
custom_info_err_no_output = 无输出
|
||||||
|
custom_info_err_no_output_error = ,可能有错误
|
||||||
err_alloc = 内存分配失败
|
err_alloc = 内存分配失败
|
||||||
|
err_args = 无法解析命令行参数
|
||||||
|
err_autologin_session = 未找到自动登录会话
|
||||||
err_bounds = 索引越界
|
err_bounds = 索引越界
|
||||||
|
err_brightness_change = 无法更改亮度
|
||||||
err_chdir = 无法打开home文件夹
|
err_chdir = 无法打开home文件夹
|
||||||
|
err_clock_too_long = 时钟字符串过长
|
||||||
|
err_config = 无法解析配置文件
|
||||||
|
err_crawl = 无法扫描会话目录
|
||||||
err_dgn_oob = 日志消息
|
err_dgn_oob = 日志消息
|
||||||
err_domain = 无效的域
|
err_domain = 无效的域
|
||||||
|
err_empty_password = 不允许空密码
|
||||||
|
err_envlist = 无法获取环境变量列表
|
||||||
|
err_get_active_tty = 无法获取当前活动的tty
|
||||||
|
err_hibernate = 无法执行休眠命令
|
||||||
err_hostname = 获取主机名失败
|
err_hostname = 获取主机名失败
|
||||||
|
err_inactivity = 无法执行非活动命令
|
||||||
|
err_lock_state = 无法获取锁定状态
|
||||||
|
err_log = 无法打开日志文件
|
||||||
err_mlock = 锁定密码存储器失败
|
err_mlock = 锁定密码存储器失败
|
||||||
err_null = 空指针
|
err_null = 空指针
|
||||||
|
err_numlock = 无法设置numlock
|
||||||
err_pam = PAM事件失败
|
err_pam = PAM事件失败
|
||||||
err_pam_abort = PAM事务已中止
|
err_pam_abort = PAM事务已中止
|
||||||
err_pam_acct_expired = 帐户已过期
|
err_pam_acct_expired = 帐户已过期
|
||||||
|
|
@ -46,33 +49,34 @@ err_perm_dir = 更改当前目录失败
|
||||||
err_perm_group = 组权限降级失败
|
err_perm_group = 组权限降级失败
|
||||||
err_perm_user = 用户权限降级失败
|
err_perm_user = 用户权限降级失败
|
||||||
err_pwnam = 获取用户信息失败
|
err_pwnam = 获取用户信息失败
|
||||||
|
err_sleep = 无法执行睡眠命令
|
||||||
|
err_start = 无法执行启动命令
|
||||||
|
err_battery = 无法加载电池状态
|
||||||
|
err_switch_tty = 无法切换tty
|
||||||
|
err_tty_ctrl = tty控制转移失败
|
||||||
|
err_no_users = 未找到用户
|
||||||
|
err_uid_range = 无法动态获取uid范围
|
||||||
err_user_gid = 设置用户GID失败
|
err_user_gid = 设置用户GID失败
|
||||||
err_user_init = 初始化用户失败
|
err_user_init = 初始化用户失败
|
||||||
err_user_uid = 设置用户UID失败
|
err_user_uid = 设置用户UID失败
|
||||||
|
err_xauth = xauth命令失败
|
||||||
|
err_xcb_conn = xcb连接失败
|
||||||
err_xsessions_dir = 找不到会话文件夹
|
err_xsessions_dir = 找不到会话文件夹
|
||||||
err_xsessions_open = 无法打开会话文件夹
|
err_xsessions_open = 无法打开会话文件夹
|
||||||
|
hibernate = 休眠
|
||||||
|
insert = 插入
|
||||||
login = 登录
|
login = 登录
|
||||||
logout = 注销
|
logout = 注销
|
||||||
|
no_x11_support = x11支持在编译时被禁用
|
||||||
|
normal = 正常
|
||||||
numlock = 数字锁定
|
numlock = 数字锁定
|
||||||
|
other = 其他
|
||||||
password = 密码
|
password = 密码
|
||||||
|
restart = 重启
|
||||||
shell = shell
|
shell = shell
|
||||||
|
shutdown = 关机
|
||||||
|
sleep = 睡眠
|
||||||
|
toggle_password = 显示/隐藏密码
|
||||||
wayland = wayland
|
wayland = wayland
|
||||||
x11 = x11
|
x11 = x11
|
||||||
xinitrc = xinitrc
|
xinitrc = xinitrc
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
authenticating = 正在驗證...
|
||||||
|
brightness_down = 降低亮度
|
||||||
|
brightness_up = 提高亮度
|
||||||
|
capslock = 大寫鎖定
|
||||||
|
custom = 自訂
|
||||||
|
custom_info_err_output_long = 輸出過長
|
||||||
|
custom_info_err_no_output = 無輸出
|
||||||
|
custom_info_err_no_output_error = ,可能有錯誤
|
||||||
|
err_alloc = 記憶體配置失敗
|
||||||
|
err_args = 無法解析命令列參數
|
||||||
|
err_autologin_session = 找不到自動登入工作階段
|
||||||
|
err_bounds = 索引超出範圍
|
||||||
|
err_brightness_change = 無法變更亮度
|
||||||
|
err_chdir = 無法開啟家目錄
|
||||||
|
err_clock_too_long = 時鐘字串過長
|
||||||
|
err_config = 無法解析設定檔
|
||||||
|
err_crawl = 無法掃描工作階段目錄
|
||||||
|
err_dgn_oob = 日誌訊息
|
||||||
|
err_domain = 無效的網域
|
||||||
|
err_empty_password = 不允許空密碼
|
||||||
|
err_envlist = 無法取得環境變數清單
|
||||||
|
err_get_active_tty = 無法取得目前使用中的 tty
|
||||||
|
err_hibernate = 無法執行休眠命令
|
||||||
|
err_hostname = 取得主機名稱失敗
|
||||||
|
err_inactivity = 無法執行閒置命令
|
||||||
|
err_lock_state = 無法取得鎖定狀態
|
||||||
|
err_log = 無法開啟日誌檔案
|
||||||
|
err_mlock = 鎖定密碼記憶體失敗
|
||||||
|
err_null = 空指標
|
||||||
|
err_numlock = 無法設定 numlock
|
||||||
|
err_pam = PAM 交易失敗
|
||||||
|
err_pam_abort = PAM 交易已中止
|
||||||
|
err_pam_acct_expired = 帳號已過期
|
||||||
|
err_pam_auth = 驗證錯誤
|
||||||
|
err_pam_authinfo_unavail = 取得使用者資訊失敗
|
||||||
|
err_pam_authok_reqd = 金鑰已過期
|
||||||
|
err_pam_buf = 記憶體緩衝區錯誤
|
||||||
|
err_pam_cred_err = 設定憑證失敗
|
||||||
|
err_pam_cred_expired = 憑證已過期
|
||||||
|
err_pam_cred_insufficient = 憑證不足
|
||||||
|
err_pam_cred_unavail = 無法取得憑證
|
||||||
|
err_pam_maxtries = 已達到最大嘗試次數限制
|
||||||
|
err_pam_perm_denied = 拒絕存取
|
||||||
|
err_pam_session = 工作階段錯誤
|
||||||
|
err_pam_sys = 系統錯誤
|
||||||
|
err_pam_user_unknown = 未知的使用者
|
||||||
|
err_path = 無法設定路徑
|
||||||
|
err_perm_dir = 變更目前目錄失敗
|
||||||
|
err_perm_group = 群組權限降級失敗
|
||||||
|
err_perm_user = 使用者權限降級失敗
|
||||||
|
err_pwnam = 取得使用者資訊失敗
|
||||||
|
err_sleep = 無法執行睡眠命令
|
||||||
|
err_start = 無法執行啟動命令
|
||||||
|
err_battery = 無法載入電池狀態
|
||||||
|
err_switch_tty = 無法切換 tty
|
||||||
|
err_tty_ctrl = tty 控制權移轉失敗
|
||||||
|
err_no_users = 找不到使用者
|
||||||
|
err_uid_range = 無法動態取得 uid 範圍
|
||||||
|
err_user_gid = 設定使用者 GID 失敗
|
||||||
|
err_user_init = 初始化使用者失敗
|
||||||
|
err_user_uid = 設定使用者 UID 失敗
|
||||||
|
err_xauth = xauth 命令失敗
|
||||||
|
err_xcb_conn = xcb 連線失敗
|
||||||
|
err_xsessions_dir = 找不到工作階段資料夾
|
||||||
|
err_xsessions_open = 無法開啟工作階段資料夾
|
||||||
|
hibernate = 休眠
|
||||||
|
insert = 插入
|
||||||
|
login = 登入
|
||||||
|
logout = 登出
|
||||||
|
no_x11_support = x11 支援已在編譯時停用
|
||||||
|
normal = 正常
|
||||||
|
numlock = 數字鎖定
|
||||||
|
other = 其他
|
||||||
|
password = 密碼
|
||||||
|
restart = 重新啟動
|
||||||
|
shell = shell
|
||||||
|
shutdown = 關機
|
||||||
|
sleep = 睡眠
|
||||||
|
toggle_password = 顯示/隱藏密碼
|
||||||
|
wayland = wayland
|
||||||
|
x11 = x11
|
||||||
|
xinitrc = xinitrc
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
[Unit]
|
||||||
|
Description=TUI display manager using KMSCON
|
||||||
|
After=systemd-user-sessions.service plymouth-quit-wait.service
|
||||||
|
After=kmsconvt@%i.service
|
||||||
|
Conflicts=kmsconvt@%i.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=$PREFIX_DIRECTORY/bin/kmscon --term=linux --font-engine unifont --vt=%I --login -- $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME --use-kmscon-vt
|
||||||
|
StandardInput=tty
|
||||||
|
UtmpIdentifier=%I
|
||||||
|
TTYPath=/dev/%I
|
||||||
|
TTYReset=yes
|
||||||
|
TTYVHangup=yes
|
||||||
|
TTYVTDisallocate=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=TUI display manager
|
Description=TUI display manager
|
||||||
After=systemd-user-sessions.service plymouth-quit-wait.service
|
After=systemd-user-sessions.service plymouth-quit-wait.service
|
||||||
After=getty@%I.service
|
After=getty@%i.service
|
||||||
Conflicts=getty@%I.service
|
Conflicts=getty@%i.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=idle
|
Type=idle
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# This file is executed when starting Ly (before the TTY is taken control of)
|
||||||
|
# Custom startup code can be placed in this file or the start_cmd var can be pointed to a different file
|
||||||
|
|
||||||
|
|
||||||
|
# Uncomment the example below for an example of changing the default TTY colors to an alternitive palette on linux
|
||||||
|
# Colors are in red/green/blue hex (the current colors are a brighter palette than default)
|
||||||
|
#
|
||||||
|
# if [ "$TERM" = "linux" ]; then
|
||||||
|
# BLACK="232323"
|
||||||
|
# DARK_RED="D75F5F"
|
||||||
|
# DARK_GREEN="87AF5F"
|
||||||
|
# DARK_YELLOW="D7AF87"
|
||||||
|
# DARK_BLUE="8787AF"
|
||||||
|
# DARK_MAGENTA="BD53A5"
|
||||||
|
# DARK_CYAN="5FAFAF"
|
||||||
|
# LIGHT_GRAY="E5E5E5"
|
||||||
|
# DARK_GRAY="2B2B2B"
|
||||||
|
# RED="E33636"
|
||||||
|
# GREEN="98E34D"
|
||||||
|
# YELLOW="FFD75F"
|
||||||
|
# BLUE="7373C9"
|
||||||
|
# MAGENTA="D633B2"
|
||||||
|
# CYAN="44C9C9"
|
||||||
|
# WHITE="FFFFFF"
|
||||||
|
|
||||||
|
# COLORS="${BLACK} ${DARK_RED} ${DARK_GREEN} ${DARK_YELLOW} ${DARK_BLUE} ${DARK_MAGENTA} ${DARK_CYAN} ${LIGHT_GRAY} ${DARK_GRAY} ${RED} ${GREEN} ${YELLOW} ${BLUE} ${MAGENTA} ${CYAN} ${WHITE}"
|
||||||
|
|
||||||
|
# i=0
|
||||||
|
# while [ $i -lt 16 ]; do
|
||||||
|
# printf "\033]P%x%s" ${i} "$(echo "$COLORS" | cut -d ' ' -f$(( i + 1)))"
|
||||||
|
|
||||||
|
# i=$(( i + 1 ))
|
||||||
|
# done
|
||||||
|
|
||||||
|
# clear # for fixing background artifacting after changing color
|
||||||
|
# fi
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
const enums = @import("enums.zig");
|
const ini = @import("ly-ui").ly_core.ini;
|
||||||
const ini = @import("zigini");
|
|
||||||
|
|
||||||
const DisplayServer = enums.DisplayServer;
|
|
||||||
const Ini = ini.Ini;
|
const Ini = ini.Ini;
|
||||||
|
|
||||||
|
const enums = @import("enums.zig");
|
||||||
|
const DisplayServer = enums.DisplayServer;
|
||||||
|
|
||||||
pub const DesktopEntry = struct {
|
pub const DesktopEntry = struct {
|
||||||
Exec: []const u8 = "",
|
Exec: []const u8 = "",
|
||||||
Name: []const u8 = "",
|
Name: []const u8 = "",
|
||||||
|
|
@ -14,9 +14,9 @@ pub const DesktopEntry = struct {
|
||||||
pub const Entry = struct { @"Desktop Entry": DesktopEntry = .{} };
|
pub const Entry = struct { @"Desktop Entry": DesktopEntry = .{} };
|
||||||
|
|
||||||
entry_ini: ?Ini(Entry) = null,
|
entry_ini: ?Ini(Entry) = null,
|
||||||
|
file_name: []const u8 = "",
|
||||||
name: []const u8 = "",
|
name: []const u8 = "",
|
||||||
xdg_session_desktop: ?[]const u8 = null,
|
xdg_session_desktop: ?[]const u8 = null,
|
||||||
xdg_session_desktop_owned: bool = false,
|
|
||||||
xdg_desktop_names: ?[]const u8 = null,
|
xdg_desktop_names: ?[]const u8 = null,
|
||||||
cmd: ?[]const u8 = null,
|
cmd: ?[]const u8 = null,
|
||||||
specifier: []const u8 = "",
|
specifier: []const u8 = "",
|
||||||
|
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const LogFile = @This();
|
|
||||||
|
|
||||||
path: []const u8,
|
|
||||||
could_open_log_file: bool = undefined,
|
|
||||||
file: std.fs.File = undefined,
|
|
||||||
buffer: []u8,
|
|
||||||
file_writer: std.fs.File.Writer = undefined,
|
|
||||||
|
|
||||||
pub fn init(path: []const u8, buffer: []u8) !LogFile {
|
|
||||||
var log_file = LogFile{ .path = path, .buffer = buffer };
|
|
||||||
log_file.could_open_log_file = try openLogFile(path, &log_file);
|
|
||||||
return log_file;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reinit(self: *LogFile) !void {
|
|
||||||
self.could_open_log_file = try openLogFile(self.path, self);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *LogFile) void {
|
|
||||||
self.file_writer.interface.flush() catch {};
|
|
||||||
self.file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn openLogFile(path: []const u8, log_file: *LogFile) !bool {
|
|
||||||
var could_open_log_file = true;
|
|
||||||
open_log_file: {
|
|
||||||
log_file.file = std.fs.cwd().openFile(path, .{ .mode = .write_only }) catch std.fs.cwd().createFile(path, .{ .mode = 0o666 }) catch {
|
|
||||||
// If we could neither open an existing log file nor create a new
|
|
||||||
// one, abort.
|
|
||||||
could_open_log_file = false;
|
|
||||||
break :open_log_file;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!could_open_log_file) {
|
|
||||||
log_file.file = try std.fs.openFileAbsolute("/dev/null", .{ .mode = .write_only });
|
|
||||||
}
|
|
||||||
|
|
||||||
var log_file_writer = log_file.file.writer(log_file.buffer);
|
|
||||||
|
|
||||||
// Seek to the end of the log file
|
|
||||||
if (could_open_log_file) {
|
|
||||||
const stat = try log_file.file.stat();
|
|
||||||
try log_file_writer.seekTo(stat.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
log_file.file_writer = log_file_writer;
|
|
||||||
return could_open_log_file;
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const ErrInt = std.meta.Int(.unsigned, @bitSizeOf(anyerror));
|
|
||||||
|
|
||||||
const ErrorHandler = packed struct {
|
|
||||||
has_error: bool = false,
|
|
||||||
err_int: ErrInt = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
const SharedError = @This();
|
|
||||||
|
|
||||||
data: []align(std.heap.page_size_min) u8,
|
|
||||||
|
|
||||||
pub fn init() !SharedError {
|
|
||||||
const data = try std.posix.mmap(null, @sizeOf(ErrorHandler), std.posix.PROT.READ | std.posix.PROT.WRITE, .{ .TYPE = .SHARED, .ANONYMOUS = true }, -1, 0);
|
|
||||||
|
|
||||||
return .{ .data = data };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *SharedError) void {
|
|
||||||
std.posix.munmap(self.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn writeError(self: SharedError, err: anyerror) void {
|
|
||||||
var buf_stream = std.io.fixedBufferStream(self.data);
|
|
||||||
const writer = buf_stream.writer();
|
|
||||||
writer.writeStruct(ErrorHandler{ .has_error = true, .err_int = @intFromError(err) }) catch {};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn readError(self: SharedError) ?anyerror {
|
|
||||||
var buf_stream = std.io.fixedBufferStream(self.data);
|
|
||||||
const reader = buf_stream.reader();
|
|
||||||
const err_handler = try reader.readStruct(ErrorHandler);
|
|
||||||
|
|
||||||
if (err_handler.has_error)
|
|
||||||
return @errorFromInt(err_handler.err_int);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const math = std.math;
|
||||||
|
|
||||||
|
const ly_ui = @import("ly-ui");
|
||||||
|
const Cell = ly_ui.Cell;
|
||||||
|
const TerminalBuffer = ly_ui.TerminalBuffer;
|
||||||
|
const Widget = ly_ui.Widget;
|
||||||
|
|
||||||
|
const Cascade = @This();
|
||||||
|
|
||||||
|
io: std.Io,
|
||||||
|
instance: ?Widget = null,
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
current_auth_fails: *u64,
|
||||||
|
max_auth_fails: u64,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
io: std.Io,
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
current_auth_fails: *u64,
|
||||||
|
max_auth_fails: u64,
|
||||||
|
) Cascade {
|
||||||
|
return .{
|
||||||
|
.io = io,
|
||||||
|
.instance = null,
|
||||||
|
.buffer = buffer,
|
||||||
|
.current_auth_fails = current_auth_fails,
|
||||||
|
.max_auth_fails = max_auth_fails,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn widget(self: *Cascade) *Widget {
|
||||||
|
if (self.instance) |*instance| return instance;
|
||||||
|
self.instance = Widget.init(
|
||||||
|
"Cascade",
|
||||||
|
null,
|
||||||
|
self,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
draw,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
return &self.instance.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(self: *Cascade) void {
|
||||||
|
while (self.current_auth_fails.* >= self.max_auth_fails) {
|
||||||
|
self.io.sleep(.fromMilliseconds(10), .real) catch {};
|
||||||
|
|
||||||
|
var changed = false;
|
||||||
|
var y = self.buffer.height - 2;
|
||||||
|
|
||||||
|
while (y > 0) : (y -= 1) {
|
||||||
|
for (0..self.buffer.width) |x| {
|
||||||
|
const cell = TerminalBuffer.getCell(x, y - 1);
|
||||||
|
const cell_under = TerminalBuffer.getCell(x, y);
|
||||||
|
|
||||||
|
// This shouldn't happen under normal circumstances, but because
|
||||||
|
// this is a *secret* animation, there's no need to care that much
|
||||||
|
if (cell == null or cell_under == null) continue;
|
||||||
|
|
||||||
|
const char: u8 = @truncate(cell.?.ch);
|
||||||
|
if (std.ascii.isWhitespace(char)) continue;
|
||||||
|
|
||||||
|
const char_under: u8 = @truncate(cell_under.?.ch);
|
||||||
|
if (!std.ascii.isWhitespace(char_under)) continue;
|
||||||
|
|
||||||
|
changed = true;
|
||||||
|
|
||||||
|
if ((self.buffer.random.int(u16) % 10) > 7) continue;
|
||||||
|
|
||||||
|
cell.?.put(x, y) catch {};
|
||||||
|
|
||||||
|
var space = Cell.init(
|
||||||
|
' ',
|
||||||
|
cell_under.?.fg,
|
||||||
|
cell_under.?.bg,
|
||||||
|
);
|
||||||
|
space.put(x, y - 1) catch {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!changed) {
|
||||||
|
self.io.sleep(.fromSeconds(7), .real) catch {};
|
||||||
|
self.current_auth_fails.* = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalBuffer.presentBuffer() catch {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,17 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Animation = @import("../tui/Animation.zig");
|
const math = std.math;
|
||||||
const Cell = @import("../tui/Cell.zig");
|
|
||||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
const ly_ui = @import("ly-ui");
|
||||||
|
const Cell = ly_ui.Cell;
|
||||||
|
const TerminalBuffer = ly_ui.TerminalBuffer;
|
||||||
|
const Widget = ly_ui.Widget;
|
||||||
|
|
||||||
|
const ly_core = ly_ui.ly_core;
|
||||||
|
const interop = ly_core.interop;
|
||||||
|
const TimeOfDay = interop.TimeOfDay;
|
||||||
|
|
||||||
const ColorMix = @This();
|
const ColorMix = @This();
|
||||||
|
|
||||||
const math = std.math;
|
|
||||||
const Vec2 = @Vector(2, f32);
|
const Vec2 = @Vector(2, f32);
|
||||||
|
|
||||||
const time_scale: f32 = 0.01;
|
const time_scale: f32 = 0.01;
|
||||||
|
|
@ -15,15 +21,33 @@ fn length(vec: Vec2) f32 {
|
||||||
return math.sqrt(vec[0] * vec[0] + vec[1] * vec[1]);
|
return math.sqrt(vec[0] * vec[0] + vec[1] * vec[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instance: ?Widget = null,
|
||||||
|
start_time: TimeOfDay,
|
||||||
terminal_buffer: *TerminalBuffer,
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
animate: *bool,
|
||||||
|
timeout_sec: u12,
|
||||||
|
frame_delay: u16,
|
||||||
frames: u64,
|
frames: u64,
|
||||||
pattern_cos_mod: f32,
|
pattern_cos_mod: f32,
|
||||||
pattern_sin_mod: f32,
|
pattern_sin_mod: f32,
|
||||||
palette: [palette_len]Cell,
|
palette: [palette_len]Cell,
|
||||||
|
|
||||||
pub fn init(terminal_buffer: *TerminalBuffer, col1: u32, col2: u32, col3: u32) ColorMix {
|
pub fn init(
|
||||||
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
col1: u32,
|
||||||
|
col2: u32,
|
||||||
|
col3: u32,
|
||||||
|
animate: *bool,
|
||||||
|
timeout_sec: u12,
|
||||||
|
frame_delay: u16,
|
||||||
|
) !ColorMix {
|
||||||
return .{
|
return .{
|
||||||
|
.instance = null,
|
||||||
|
.start_time = try interop.getTimeOfDay(),
|
||||||
.terminal_buffer = terminal_buffer,
|
.terminal_buffer = terminal_buffer,
|
||||||
|
.animate = animate,
|
||||||
|
.timeout_sec = timeout_sec,
|
||||||
|
.frame_delay = frame_delay,
|
||||||
.frames = 0,
|
.frames = 0,
|
||||||
.pattern_cos_mod = terminal_buffer.random.float(f32) * math.pi * 2.0,
|
.pattern_cos_mod = terminal_buffer.random.float(f32) * math.pi * 2.0,
|
||||||
.pattern_sin_mod = terminal_buffer.random.float(f32) * math.pi * 2.0,
|
.pattern_sin_mod = terminal_buffer.random.float(f32) * math.pi * 2.0,
|
||||||
|
|
@ -44,15 +68,25 @@ pub fn init(terminal_buffer: *TerminalBuffer, col1: u32, col2: u32, col3: u32) C
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn animation(self: *ColorMix) Animation {
|
pub fn widget(self: *ColorMix) *Widget {
|
||||||
return Animation.init(self, deinit, realloc, draw);
|
if (self.instance) |*instance| return instance;
|
||||||
|
self.instance = Widget.init(
|
||||||
|
"ColorMix",
|
||||||
|
null,
|
||||||
|
self,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
draw,
|
||||||
|
update,
|
||||||
|
null,
|
||||||
|
calculateTimeout,
|
||||||
|
);
|
||||||
|
return &self.instance.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(_: *ColorMix) void {}
|
|
||||||
|
|
||||||
fn realloc(_: *ColorMix) anyerror!void {}
|
|
||||||
|
|
||||||
fn draw(self: *ColorMix) void {
|
fn draw(self: *ColorMix) void {
|
||||||
|
if (!self.animate.*) return;
|
||||||
|
|
||||||
self.frames +%= 1;
|
self.frames +%= 1;
|
||||||
const time: f32 = @as(f32, @floatFromInt(self.frames)) * time_scale;
|
const time: f32 = @as(f32, @floatFromInt(self.frames)) * time_scale;
|
||||||
|
|
||||||
|
|
@ -79,8 +113,20 @@ fn draw(self: *ColorMix) void {
|
||||||
uv -= @splat(1.0 * math.cos(uv[0] + uv[1]) - math.sin(uv[0] * 0.7 - uv[1]));
|
uv -= @splat(1.0 * math.cos(uv[0] + uv[1]) - math.sin(uv[0] * 0.7 - uv[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
const cell = self.palette[@as(usize, @intFromFloat(math.floor(length(uv) * 5.0))) % palette_len];
|
const cell = self.palette[@as(usize, @trunc(math.floor(length(uv) * 5.0))) % palette_len];
|
||||||
cell.put(x, y);
|
cell.put(x, y) catch {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update(self: *ColorMix, _: *anyopaque) !void {
|
||||||
|
const time = try interop.getTimeOfDay();
|
||||||
|
|
||||||
|
if (self.timeout_sec > 0 and time.seconds - self.start_time.seconds > self.timeout_sec) {
|
||||||
|
self.animate.* = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculateTimeout(self: *ColorMix, _: *anyopaque) !?usize {
|
||||||
|
return self.frame_delay;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Animation = @import("../tui/Animation.zig");
|
|
||||||
const Cell = @import("../tui/Cell.zig");
|
const ly_ui = @import("ly-ui");
|
||||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
const Cell = ly_ui.Cell;
|
||||||
|
const TerminalBuffer = ly_ui.TerminalBuffer;
|
||||||
|
const Widget = ly_ui.Widget;
|
||||||
|
|
||||||
|
const ly_core = ly_ui.ly_core;
|
||||||
|
const interop = ly_core.interop;
|
||||||
|
const TimeOfDay = interop.TimeOfDay;
|
||||||
|
|
||||||
const Doom = @This();
|
const Doom = @This();
|
||||||
|
|
||||||
|
|
@ -10,14 +16,30 @@ pub const STEPS = 12;
|
||||||
pub const HEIGHT_MAX = 9;
|
pub const HEIGHT_MAX = 9;
|
||||||
pub const SPREAD_MAX = 4;
|
pub const SPREAD_MAX = 4;
|
||||||
|
|
||||||
|
instance: ?Widget = null,
|
||||||
|
start_time: TimeOfDay,
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
terminal_buffer: *TerminalBuffer,
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
animate: *bool,
|
||||||
|
timeout_sec: u12,
|
||||||
|
frame_delay: u16,
|
||||||
buffer: []u8,
|
buffer: []u8,
|
||||||
height: u8,
|
height: u8,
|
||||||
spread: u8,
|
spread: u8,
|
||||||
fire: [STEPS + 1]Cell,
|
fire: [STEPS + 1]Cell,
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, top_color: u32, middle_color: u32, bottom_color: u32, fire_height: u8, fire_spread: u8) !Doom {
|
pub fn init(
|
||||||
|
allocator: Allocator,
|
||||||
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
top_color: u32,
|
||||||
|
middle_color: u32,
|
||||||
|
bottom_color: u32,
|
||||||
|
fire_height: u8,
|
||||||
|
fire_spread: u8,
|
||||||
|
animate: *bool,
|
||||||
|
timeout_sec: u12,
|
||||||
|
frame_delay: u16,
|
||||||
|
) !Doom {
|
||||||
const buffer = try allocator.alloc(u8, terminal_buffer.width * terminal_buffer.height);
|
const buffer = try allocator.alloc(u8, terminal_buffer.width * terminal_buffer.height);
|
||||||
initBuffer(buffer, terminal_buffer.width);
|
initBuffer(buffer, terminal_buffer.width);
|
||||||
|
|
||||||
|
|
@ -39,8 +61,13 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, top_color: u
|
||||||
};
|
};
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
|
.instance = null,
|
||||||
|
.start_time = try interop.getTimeOfDay(),
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.terminal_buffer = terminal_buffer,
|
.terminal_buffer = terminal_buffer,
|
||||||
|
.animate = animate,
|
||||||
|
.timeout_sec = timeout_sec,
|
||||||
|
.frame_delay = frame_delay,
|
||||||
.buffer = buffer,
|
.buffer = buffer,
|
||||||
.height = @min(HEIGHT_MAX, fire_height),
|
.height = @min(HEIGHT_MAX, fire_height),
|
||||||
.spread = @min(SPREAD_MAX, fire_spread),
|
.spread = @min(SPREAD_MAX, fire_spread),
|
||||||
|
|
@ -48,21 +75,35 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, top_color: u
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn animation(self: *Doom) Animation {
|
pub fn widget(self: *Doom) *Widget {
|
||||||
return Animation.init(self, deinit, realloc, draw);
|
if (self.instance) |*instance| return instance;
|
||||||
|
self.instance = Widget.init(
|
||||||
|
"Doom",
|
||||||
|
null,
|
||||||
|
self,
|
||||||
|
deinit,
|
||||||
|
realloc,
|
||||||
|
draw,
|
||||||
|
update,
|
||||||
|
null,
|
||||||
|
calculateTimeout,
|
||||||
|
);
|
||||||
|
return &self.instance.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: *Doom) void {
|
fn deinit(self: *Doom) void {
|
||||||
self.allocator.free(self.buffer);
|
self.allocator.free(self.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realloc(self: *Doom) anyerror!void {
|
fn realloc(self: *Doom) !void {
|
||||||
const buffer = try self.allocator.realloc(self.buffer, self.terminal_buffer.width * self.terminal_buffer.height);
|
const buffer = try self.allocator.realloc(self.buffer, self.terminal_buffer.width * self.terminal_buffer.height);
|
||||||
initBuffer(buffer, self.terminal_buffer.width);
|
initBuffer(buffer, self.terminal_buffer.width);
|
||||||
self.buffer = buffer;
|
self.buffer = buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(self: *Doom) void {
|
fn draw(self: *Doom) void {
|
||||||
|
if (!self.animate.*) return;
|
||||||
|
|
||||||
for (0..self.terminal_buffer.width) |x| {
|
for (0..self.terminal_buffer.width) |x| {
|
||||||
// We start from 1 so that we always have the topmost line when spreading fire
|
// We start from 1 so that we always have the topmost line when spreading fire
|
||||||
for (1..self.terminal_buffer.height) |y| {
|
for (1..self.terminal_buffer.height) |y| {
|
||||||
|
|
@ -88,13 +129,13 @@ fn draw(self: *Doom) void {
|
||||||
// Send known fire levels to terminal buffer
|
// Send known fire levels to terminal buffer
|
||||||
const from_cell = self.fire[level_buf_from];
|
const from_cell = self.fire[level_buf_from];
|
||||||
const to_cell = self.fire[level_buf_to];
|
const to_cell = self.fire[level_buf_to];
|
||||||
from_cell.put(x, y);
|
from_cell.put(x, y) catch {};
|
||||||
to_cell.put(to_x, to_y);
|
to_cell.put(to_x, to_y) catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw bottom line (fire source)
|
// Draw bottom line (fire source)
|
||||||
const src_cell = self.fire[STEPS];
|
const src_cell = self.fire[STEPS];
|
||||||
src_cell.put(x, self.terminal_buffer.height - 1);
|
src_cell.put(x, self.terminal_buffer.height - 1) catch {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,3 +149,15 @@ fn initBuffer(buffer: []u8, width: usize) void {
|
||||||
@memset(slice_start, 0);
|
@memset(slice_start, 0);
|
||||||
@memset(slice_end, STEPS);
|
@memset(slice_end, STEPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update(self: *Doom, _: *anyopaque) !void {
|
||||||
|
const time = try interop.getTimeOfDay();
|
||||||
|
|
||||||
|
if (self.timeout_sec > 0 and time.seconds - self.start_time.seconds > self.timeout_sec) {
|
||||||
|
self.animate.* = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculateTimeout(self: *Doom, _: *anyopaque) !?usize {
|
||||||
|
return self.frame_delay;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const Animation = @import("../tui/Animation.zig");
|
|
||||||
|
|
||||||
const Dummy = @This();
|
|
||||||
|
|
||||||
pub fn animation(self: *Dummy) Animation {
|
|
||||||
return Animation.init(self, deinit, realloc, draw);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deinit(_: *Dummy) void {}
|
|
||||||
|
|
||||||
fn realloc(_: *Dummy) anyerror!void {}
|
|
||||||
|
|
||||||
fn draw(_: *Dummy) void {}
|
|
||||||
|
|
@ -1,32 +1,23 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Animation = @import("../tui/Animation.zig");
|
|
||||||
const Cell = @import("../tui/Cell.zig");
|
|
||||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
|
||||||
const Color = TerminalBuffer.Color;
|
|
||||||
const Styling = TerminalBuffer.Styling;
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Json = std.json;
|
const Json = std.json;
|
||||||
const eql = std.mem.eql;
|
const eql = std.mem.eql;
|
||||||
const flate = std.compress.flate;
|
const flate = std.compress.flate;
|
||||||
|
|
||||||
fn read_decompress_file(allocator: Allocator, file_path: []const u8) ![]u8 {
|
const ly_ui = @import("ly-ui");
|
||||||
const file_buffer = std.fs.cwd().openFile(file_path, .{}) catch {
|
const Cell = ly_ui.Cell;
|
||||||
return error.FileNotFound;
|
const TerminalBuffer = ly_ui.TerminalBuffer;
|
||||||
};
|
const Color = TerminalBuffer.Color;
|
||||||
defer file_buffer.close();
|
const Styling = TerminalBuffer.Styling;
|
||||||
|
const Widget = ly_ui.Widget;
|
||||||
|
|
||||||
var file_reader_buffer: [4096]u8 = undefined;
|
const ly_core = ly_ui.ly_core;
|
||||||
var decompress_buffer: [flate.max_window_len]u8 = undefined;
|
const interop = ly_core.interop;
|
||||||
|
const TimeOfDay = interop.TimeOfDay;
|
||||||
|
const LogFile = ly_core.LogFile;
|
||||||
|
|
||||||
var file_reader = file_buffer.reader(&file_reader_buffer);
|
const enums = @import("../enums.zig");
|
||||||
var decompress: flate.Decompress = .init(&file_reader.interface, .gzip, &decompress_buffer);
|
const DurOffsetAlignment = enums.DurOffsetAlignment;
|
||||||
|
|
||||||
const file_decompressed = decompress.reader.allocRemaining(allocator, .unlimited) catch {
|
|
||||||
return error.NotValidFile;
|
|
||||||
};
|
|
||||||
|
|
||||||
return file_decompressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Frame = struct {
|
const Frame = struct {
|
||||||
frameNumber: i32,
|
frameNumber: i32,
|
||||||
|
|
@ -52,7 +43,7 @@ const Frame = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://github.com/cmang/durdraw/blob/0.29.0/durformat.md
|
// https://github.com/cmang/durdraw/blob/0.29.0/durformat.md
|
||||||
const DurFormat = struct {
|
const DurFormatRaw = struct {
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
formatVersion: ?i64 = null,
|
formatVersion: ?i64 = null,
|
||||||
colorFormat: ?[]const u8 = null,
|
colorFormat: ?[]const u8 = null,
|
||||||
|
|
@ -62,38 +53,48 @@ const DurFormat = struct {
|
||||||
lines: ?i64 = null,
|
lines: ?i64 = null,
|
||||||
frames: std.ArrayList(Frame) = undefined,
|
frames: std.ArrayList(Frame) = undefined,
|
||||||
|
|
||||||
pub fn valid(self: *DurFormat) bool {
|
// Validate data and return a valid DurFormat
|
||||||
if (self.formatVersion != null and
|
// Consumes `self`, making it unusable after
|
||||||
self.colorFormat != null and
|
pub fn validate(self: *DurFormatRaw) !DurFormat {
|
||||||
self.encoding != null and
|
|
||||||
self.framerate != null and
|
|
||||||
self.columns != null and
|
|
||||||
self.lines != null and
|
|
||||||
self.frames.items.len >= 1)
|
|
||||||
{
|
|
||||||
// v8 may have breaking changes like changing the colormap xy direction
|
// v8 may have breaking changes like changing the colormap xy direction
|
||||||
// (https://github.com/cmang/durdraw/issues/24)
|
// (https://github.com/cmang/durdraw/issues/24)
|
||||||
if (self.formatVersion.? != 7) return false;
|
const format_version = self.formatVersion orelse return error.MissingFieldVersion;
|
||||||
|
if (format_version != 7) return error.UnsupportedVersion;
|
||||||
|
|
||||||
|
const color_format_str = self.colorFormat orelse return error.MissingFieldColorFormat;
|
||||||
// Code currently only supports 16 and 256 color format only
|
// Code currently only supports 16 and 256 color format only
|
||||||
if (!(eql(u8, "16", self.colorFormat.?) or eql(u8, "256", self.colorFormat.?)))
|
const color_format: DurColorFormat =
|
||||||
return false;
|
if (eql(u8, color_format_str, "16")) .@"16" else if (eql(u8, color_format_str, "256")) .@"256" else return error.UnsupportedColorFormat;
|
||||||
|
|
||||||
|
const encoding_str = self.encoding orelse return error.MissingFieldEncoding;
|
||||||
// Code currently supports only utf-8 encoding
|
// Code currently supports only utf-8 encoding
|
||||||
if (!eql(u8, self.encoding.?, "utf-8")) return false;
|
const encoding: DurEncoding = if (eql(u8, encoding_str, "utf-8")) .utf_8 else return error.UnsupportedEncoding;
|
||||||
|
|
||||||
|
if (self.framerate == null) return error.MissingFieldFramerate;
|
||||||
|
if (self.framerate.? <= 0) return error.InvalidFramerate;
|
||||||
|
const framerate: f64 = self.framerate.?;
|
||||||
|
|
||||||
// Sanity check on file
|
// Sanity check on file
|
||||||
if (self.columns.? <= 0) return false;
|
if (self.columns == null or self.lines == null) return error.MissingDimensions;
|
||||||
if (self.lines.? <= 0) return false;
|
const columns = std.math.cast(u32, self.columns.?) orelse return error.InvalidColumnCount;
|
||||||
if (self.framerate.? < 0) return false;
|
const lines = std.math.cast(u32, self.lines.?) orelse return error.InvalidLineCount;
|
||||||
|
|
||||||
return true;
|
if (self.frames.items.len == 0) return error.NoFrames;
|
||||||
|
const frames = self.frames;
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.allocator = self.allocator,
|
||||||
|
.formatVersion = format_version,
|
||||||
|
.colorFormat = color_format,
|
||||||
|
.encoding = encoding,
|
||||||
|
.framerate = framerate,
|
||||||
|
.columns = columns,
|
||||||
|
.lines = lines,
|
||||||
|
.frames = frames,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
fn parseFromJson(self: *DurFormatRaw, allocator: Allocator, dur_json_root: Json.Value) !void {
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_dur_from_json(self: *DurFormat, allocator: Allocator, dur_json_root: Json.Value) !void {
|
|
||||||
var dur_movie = if (dur_json_root.object.get("DurMovie")) |dm| dm.object else return error.NotValidFile;
|
var dur_movie = if (dur_json_root.object.get("DurMovie")) |dm| dm.object else return error.NotValidFile;
|
||||||
|
|
||||||
// Depending on the version, a dur file can have different json object names (ie: columns vs sizeX)
|
// Depending on the version, a dur file can have different json object names (ie: columns vs sizeX)
|
||||||
|
|
@ -140,28 +141,53 @@ const DurFormat = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_from_file(self: *DurFormat, allocator: Allocator, file_path: []const u8) !void {
|
pub fn createFromFile(self: *DurFormatRaw, allocator: Allocator, io: std.Io, file_path: []const u8) !void {
|
||||||
const file_decompressed = try read_decompress_file(allocator, file_path);
|
const file = try std.Io.Dir.cwd().openFile(io, file_path, .{});
|
||||||
defer allocator.free(file_decompressed);
|
defer file.close(io);
|
||||||
|
|
||||||
const parsed = try Json.parseFromSlice(Json.Value, allocator, file_decompressed, .{});
|
var reader_buffer: [4096]u8 = undefined;
|
||||||
defer parsed.deinit();
|
var decompress_buffer: [flate.max_window_len]u8 = undefined;
|
||||||
|
|
||||||
try parse_dur_from_json(self, allocator, parsed.value);
|
var file_reader = file.reader(io, &reader_buffer);
|
||||||
|
var decompress: flate.Decompress = .init(&file_reader.interface, .gzip, &decompress_buffer);
|
||||||
|
|
||||||
if (!self.valid()) {
|
var json_reader = Json.Reader.init(allocator, &decompress.reader);
|
||||||
return error.NotValidFile;
|
defer json_reader.deinit();
|
||||||
}
|
|
||||||
|
const json = try Json.parseFromTokenSource(Json.Value, allocator, &json_reader, .{});
|
||||||
|
defer json.deinit();
|
||||||
|
|
||||||
|
try parseFromJson(self, allocator, json.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(allocator: Allocator) DurFormat {
|
pub fn init(allocator: Allocator) DurFormatRaw {
|
||||||
return .{ .allocator = allocator };
|
return .{ .allocator = allocator };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *DurFormat) void {
|
pub fn deinit(self: *DurFormatRaw) void {
|
||||||
if (self.colorFormat) |str| self.allocator.free(str);
|
if (self.colorFormat) |str| self.allocator.free(str);
|
||||||
if (self.encoding) |str| self.allocator.free(str);
|
if (self.encoding) |str| self.allocator.free(str);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const DurColorFormat = enum {
|
||||||
|
@"16",
|
||||||
|
@"256",
|
||||||
|
};
|
||||||
|
|
||||||
|
const DurEncoding = enum { utf_8 };
|
||||||
|
|
||||||
|
const DurFormat = struct {
|
||||||
|
allocator: Allocator,
|
||||||
|
formatVersion: i64,
|
||||||
|
colorFormat: DurColorFormat,
|
||||||
|
encoding: DurEncoding,
|
||||||
|
framerate: f64,
|
||||||
|
columns: u32,
|
||||||
|
lines: u32,
|
||||||
|
frames: std.ArrayList(Frame),
|
||||||
|
|
||||||
|
pub fn deinit(self: *DurFormat) void {
|
||||||
for (self.frames.items) |frame| {
|
for (self.frames.items) |frame| {
|
||||||
frame.deinit(self.allocator);
|
frame.deinit(self.allocator);
|
||||||
}
|
}
|
||||||
|
|
@ -230,7 +256,7 @@ const durcolor_table_to_color16 = [17]u32{
|
||||||
15, // 16 bright white
|
15, // 16 bright white
|
||||||
};
|
};
|
||||||
|
|
||||||
fn sixcube_to_channel(sixcube: u32) u32 {
|
fn sixCubeToChannel(sixcube: u32) u32 {
|
||||||
// Although the range top for the extended range is 0xFF, 6 is not divisible into 0xFF,
|
// Although the range top for the extended range is 0xFF, 6 is not divisible into 0xFF,
|
||||||
// so we use 0xF0 instead with a scaler
|
// so we use 0xF0 instead with a scaler
|
||||||
const equal_divisions = 0xF0 / 6;
|
const equal_divisions = 0xF0 / 6;
|
||||||
|
|
@ -241,7 +267,7 @@ fn sixcube_to_channel(sixcube: u32) u32 {
|
||||||
return if (sixcube > 0) (sixcube * equal_divisions) + scaler else 0;
|
return if (sixcube > 0) (sixcube * equal_divisions) + scaler else 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_256_to_rgb(color_256: u32) u32 {
|
fn convert256ToRgb(color_256: u32) u32 {
|
||||||
var rgb_color: u32 = 0;
|
var rgb_color: u32 = 0;
|
||||||
|
|
||||||
// 0 - 15 is the standard color range, map to array table
|
// 0 - 15 is the standard color range, map to array table
|
||||||
|
|
@ -257,9 +283,9 @@ fn convert_256_to_rgb(color_256: u32) u32 {
|
||||||
// divide by 1 gets the height of the cube (divide 1 for clarity for what we are doing)
|
// divide by 1 gets the height of the cube (divide 1 for clarity for what we are doing)
|
||||||
// each channel can be 6 levels of brightness hence remander operation of 6
|
// each channel can be 6 levels of brightness hence remander operation of 6
|
||||||
// finally bitshift to correct rgb channel (16 for red, 8 for green, 0 for blue)
|
// finally bitshift to correct rgb channel (16 for red, 8 for green, 0 for blue)
|
||||||
rgb_color |= sixcube_to_channel(((color_256 - 16) / 36) % 6) << 16;
|
rgb_color |= sixCubeToChannel(((color_256 - 16) / 36) % 6) << 16;
|
||||||
rgb_color |= sixcube_to_channel(((color_256 - 16) / 6) % 6) << 8;
|
rgb_color |= sixCubeToChannel(((color_256 - 16) / 6) % 6) << 8;
|
||||||
rgb_color |= sixcube_to_channel(((color_256 - 16) / 1) % 6);
|
rgb_color |= sixCubeToChannel(((color_256 - 16) / 1) % 6);
|
||||||
}
|
}
|
||||||
// 232 - 255 is the grayscale range
|
// 232 - 255 is the grayscale range
|
||||||
else {
|
else {
|
||||||
|
|
@ -286,94 +312,213 @@ fn convert_256_to_rgb(color_256: u32) u32 {
|
||||||
return rgb_color;
|
return rgb_color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const UVec2 = @Vector(2, u32);
|
||||||
|
const IVec2 = @Vector(2, i64);
|
||||||
|
|
||||||
|
const VEC_X = 0;
|
||||||
|
const VEC_Y = 1;
|
||||||
|
|
||||||
const DurFile = @This();
|
const DurFile = @This();
|
||||||
|
|
||||||
|
instance: ?Widget = null,
|
||||||
|
start_time: TimeOfDay,
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
|
io: std.Io,
|
||||||
terminal_buffer: *TerminalBuffer,
|
terminal_buffer: *TerminalBuffer,
|
||||||
frames: u64,
|
|
||||||
time_previous: i64,
|
|
||||||
x_offset: u32,
|
|
||||||
y_offset: u32,
|
|
||||||
full_color: bool,
|
|
||||||
dur_movie: DurFormat,
|
dur_movie: DurFormat,
|
||||||
frame_width: u32,
|
frames: usize,
|
||||||
frame_height: u32,
|
start_pos: IVec2,
|
||||||
|
full_color: bool,
|
||||||
|
animate: *bool,
|
||||||
|
timeout_sec: u12,
|
||||||
|
frame_delay: u16,
|
||||||
frame_time: u32,
|
frame_time: u32,
|
||||||
|
time_previous: i64,
|
||||||
is_color_format_16: bool,
|
is_color_format_16: bool,
|
||||||
|
offset_alignment: DurOffsetAlignment,
|
||||||
|
offset: IVec2,
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, log_writer: *std.io.Writer, file_path: []const u8, x_offset: u32, y_offset: u32, full_color: bool) !DurFile {
|
// if the user has an even number of columns or rows, we will default to the left or higher position (e.g. 4 columns center = .x..)
|
||||||
var dur_movie: DurFormat = .init(allocator);
|
fn center(v: i64) i64 {
|
||||||
|
return @intCast(@divTrunc(v, 2) + @mod(v, 2));
|
||||||
|
}
|
||||||
|
|
||||||
// error state is recoverable when thrown to main and results in no background with Dummy in main
|
fn calculateStartPos(terminal_buffer: *TerminalBuffer, dur_movie: *DurFormat, offset_alignment: DurOffsetAlignment, offset: IVec2) IVec2 {
|
||||||
dur_movie.create_from_file(allocator, file_path) catch |err| switch (err) {
|
const buf_width: i64 = @intCast(terminal_buffer.width);
|
||||||
|
const buf_height: i64 = @intCast(terminal_buffer.height);
|
||||||
|
|
||||||
|
const movie_width: i64 = @intCast(dur_movie.columns);
|
||||||
|
const movie_height: i64 = @intCast(dur_movie.lines);
|
||||||
|
|
||||||
|
const start_pos: IVec2 = switch (offset_alignment) {
|
||||||
|
DurOffsetAlignment.center => .{ center(buf_width) - center(movie_width), center(buf_height) - center(movie_height) },
|
||||||
|
DurOffsetAlignment.topleft => .{ 0, 0 },
|
||||||
|
DurOffsetAlignment.topcenter => .{ center(buf_width) - center(movie_width), 0 },
|
||||||
|
DurOffsetAlignment.topright => .{ buf_width - movie_width, 0 },
|
||||||
|
DurOffsetAlignment.centerleft => .{ 0, center(buf_height) - center(movie_height) },
|
||||||
|
DurOffsetAlignment.centerright => .{ buf_width - movie_width, center(buf_height) - center(movie_height) },
|
||||||
|
DurOffsetAlignment.bottomleft => .{ 0, buf_height - movie_height },
|
||||||
|
DurOffsetAlignment.bottomcenter => .{ center(buf_width) - center(movie_width), buf_height - movie_height },
|
||||||
|
DurOffsetAlignment.bottomright => .{ buf_width - movie_width, buf_height - movie_height },
|
||||||
|
};
|
||||||
|
|
||||||
|
return start_pos + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
allocator: Allocator,
|
||||||
|
io: std.Io,
|
||||||
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
log_file: *LogFile,
|
||||||
|
file_path: []const u8,
|
||||||
|
offset_alignment: DurOffsetAlignment,
|
||||||
|
x_offset: i32,
|
||||||
|
y_offset: i32,
|
||||||
|
full_color: bool,
|
||||||
|
animate: *bool,
|
||||||
|
timeout_sec: u12,
|
||||||
|
frame_delay: u16,
|
||||||
|
) !DurFile {
|
||||||
|
var dur_movie_raw: DurFormatRaw = .init(allocator);
|
||||||
|
defer dur_movie_raw.deinit();
|
||||||
|
|
||||||
|
dur_movie_raw.createFromFile(allocator, io, file_path) catch |err| switch (err) {
|
||||||
error.FileNotFound => {
|
error.FileNotFound => {
|
||||||
try log_writer.print("error: dur_file was not found at: {s}\n", .{file_path});
|
try log_file.err(io, "tui", "dur_file was not found at: {s}", .{file_path});
|
||||||
return err;
|
return err;
|
||||||
},
|
},
|
||||||
error.NotValidFile => {
|
error.NotValidFile => {
|
||||||
try log_writer.print("error: dur_file loaded was invalid or not a dur file!\n", .{});
|
try log_file.err(io, "tui", "dur_file loaded was invalid or not a dur file!", .{});
|
||||||
return err;
|
return err;
|
||||||
},
|
},
|
||||||
else => return err,
|
else => return err,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var dur_movie = dur_movie_raw.validate() catch |err| switch (err) {
|
||||||
|
error.MissingFieldVersion => {
|
||||||
|
try log_file.err(io, "tui", "dur_file loaded was invalid: missing field formatVersion!", .{});
|
||||||
|
return err;
|
||||||
|
},
|
||||||
|
error.UnsupportedVersion => {
|
||||||
|
try log_file.err(io, "tui", "dur_file loaded was invalid: unsupported version ({d})!", .{dur_movie_raw.formatVersion.?});
|
||||||
|
return err;
|
||||||
|
},
|
||||||
|
error.MissingFieldColorFormat => {
|
||||||
|
try log_file.err(io, "tui", "dur_file loaded was invalid: missing field colorFormat!", .{});
|
||||||
|
return err;
|
||||||
|
},
|
||||||
|
error.UnsupportedColorFormat => {
|
||||||
|
try log_file.err(io, "tui", "dur_file loaded was invalid: unsupported colorFormat ({s})!", .{dur_movie_raw.colorFormat.?});
|
||||||
|
return err;
|
||||||
|
},
|
||||||
|
error.MissingFieldEncoding => {
|
||||||
|
try log_file.err(io, "tui", "dur_file loaded was invalid: missing field encoding!", .{});
|
||||||
|
return err;
|
||||||
|
},
|
||||||
|
error.UnsupportedEncoding => {
|
||||||
|
try log_file.err(io, "tui", "dur_file loaded was invalid: unsupported encoding ({s})!", .{dur_movie_raw.encoding.?});
|
||||||
|
return err;
|
||||||
|
},
|
||||||
|
error.MissingFieldFramerate => {
|
||||||
|
try log_file.err(io, "tui", "dur_file loaded was invalid: missing field framerate!", .{});
|
||||||
|
return err;
|
||||||
|
},
|
||||||
|
error.InvalidFramerate => {
|
||||||
|
try log_file.err(io, "tui", "dur_file loaded was invalid: negative framerate value found!", .{});
|
||||||
|
return err;
|
||||||
|
},
|
||||||
|
error.MissingDimensions => {
|
||||||
|
try log_file.err(io, "tui", "dur_file loaded was invalid: missing field(s) lines and/or columns!", .{});
|
||||||
|
return err;
|
||||||
|
},
|
||||||
|
error.InvalidColumnCount => {
|
||||||
|
try log_file.err(io, "tui", "dur_file loaded was invalid: columns value falls outside of supported range ({d})!", .{dur_movie_raw.columns.?});
|
||||||
|
return err;
|
||||||
|
},
|
||||||
|
error.InvalidLineCount => {
|
||||||
|
try log_file.err(io, "tui", "dur_file loaded was invalid: lines value falls outside of supported range ({d})!", .{dur_movie_raw.lines.?});
|
||||||
|
return err;
|
||||||
|
},
|
||||||
|
error.NoFrames => {
|
||||||
|
try log_file.err(io, "tui", "dur_file loaded was invalid: animation has no frames!", .{});
|
||||||
|
return err;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// 4 bit mode with 256 color is unsupported
|
// 4 bit mode with 256 color is unsupported
|
||||||
if (!full_color and eql(u8, dur_movie.colorFormat.?, "256")) {
|
if (!full_color and dur_movie.colorFormat == .@"256") {
|
||||||
try log_writer.print("error: dur_file can not be 256 color encoded when not using full_color option!\n", .{});
|
try log_file.err(io, "tui", "dur_file can not be 256 color encoded when not using full_color option!", .{});
|
||||||
dur_movie.deinit();
|
dur_movie.deinit();
|
||||||
return error.InvalidColorFormat;
|
return error.NotFullColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
const buf_width: u32 = @intCast(terminal_buffer.width);
|
const offset: IVec2 = .{ x_offset, y_offset };
|
||||||
const buf_height: u32 = @intCast(terminal_buffer.height);
|
|
||||||
|
|
||||||
const movie_width: u32 = @intCast(dur_movie.columns.?);
|
const start_pos = calculateStartPos(terminal_buffer, &dur_movie, offset_alignment, offset);
|
||||||
const movie_height: u32 = @intCast(dur_movie.lines.?);
|
|
||||||
|
|
||||||
// Clamp to prevent user from exceeding draw window
|
|
||||||
const x_offset_clamped = std.math.clamp(x_offset, 0, buf_width - 1);
|
|
||||||
const y_offset_clamped = std.math.clamp(y_offset, 0, buf_height - 1);
|
|
||||||
|
|
||||||
// Ensure if user offsets and frame goes offscreen, it will not overflow draw
|
|
||||||
const frame_width = if ((movie_width + x_offset_clamped) < buf_width) movie_width else buf_width - x_offset_clamped;
|
|
||||||
const frame_height = if ((movie_height + y_offset_clamped) < buf_height) movie_height else buf_height - y_offset_clamped;
|
|
||||||
|
|
||||||
// Convert dur fps to frames per ms
|
// Convert dur fps to frames per ms
|
||||||
const frame_time: u32 = @intFromFloat(1000 / dur_movie.framerate.?);
|
const frame_time: u32 = @trunc(1000 / dur_movie.framerate);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
|
.instance = null,
|
||||||
|
.start_time = try interop.getTimeOfDay(),
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
|
.io = io,
|
||||||
.terminal_buffer = terminal_buffer,
|
.terminal_buffer = terminal_buffer,
|
||||||
.frames = 0,
|
.frames = 0,
|
||||||
.time_previous = std.time.milliTimestamp(),
|
.time_previous = std.Io.Timestamp.now(io, .real).toMilliseconds(),
|
||||||
.x_offset = x_offset_clamped,
|
.start_pos = start_pos,
|
||||||
.y_offset = y_offset_clamped,
|
|
||||||
.full_color = full_color,
|
.full_color = full_color,
|
||||||
|
.animate = animate,
|
||||||
|
.timeout_sec = timeout_sec,
|
||||||
|
.frame_delay = frame_delay,
|
||||||
.dur_movie = dur_movie,
|
.dur_movie = dur_movie,
|
||||||
.frame_width = frame_width,
|
|
||||||
.frame_height = frame_height,
|
|
||||||
.frame_time = frame_time,
|
.frame_time = frame_time,
|
||||||
.is_color_format_16 = eql(u8, dur_movie.colorFormat.?, "16"),
|
.is_color_format_16 = dur_movie.colorFormat == .@"16",
|
||||||
|
.offset_alignment = offset_alignment,
|
||||||
|
.offset = offset,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn animation(self: *DurFile) Animation {
|
pub fn widget(self: *DurFile) *Widget {
|
||||||
return Animation.init(self, deinit, realloc, draw);
|
if (self.instance) |*instance| return instance;
|
||||||
|
self.instance = Widget.init(
|
||||||
|
"DurFile",
|
||||||
|
null,
|
||||||
|
self,
|
||||||
|
deinit,
|
||||||
|
realloc,
|
||||||
|
draw,
|
||||||
|
update,
|
||||||
|
null,
|
||||||
|
calculateTimeout,
|
||||||
|
);
|
||||||
|
return &self.instance.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: *DurFile) void {
|
fn deinit(self: *DurFile) void {
|
||||||
self.dur_movie.deinit();
|
self.dur_movie.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realloc(_: *DurFile) anyerror!void {}
|
fn realloc(self: *DurFile) !void {
|
||||||
|
// when terminal size changes, we need to recalculate the start_pos based on the new size
|
||||||
|
self.start_pos = calculateStartPos(self.terminal_buffer, &self.dur_movie, self.offset_alignment, self.offset);
|
||||||
|
}
|
||||||
|
|
||||||
fn draw(self: *DurFile) void {
|
fn draw(self: *DurFile) void {
|
||||||
|
if (!self.animate.*) return;
|
||||||
|
|
||||||
const current_frame = self.dur_movie.frames.items[self.frames];
|
const current_frame = self.dur_movie.frames.items[self.frames];
|
||||||
|
|
||||||
for (0..self.frame_height) |y| {
|
// y is used as an iterator in the durformat, while cell_y gives us the correct placement for the cell (same for x)
|
||||||
|
for (0..@intCast(self.dur_movie.lines)) |y| {
|
||||||
|
const cell_y = @as(i32, @intCast(y)) + self.start_pos[VEC_Y];
|
||||||
|
|
||||||
var iter = std.unicode.Utf8View.initUnchecked(current_frame.contents[y]).iterator();
|
var iter = std.unicode.Utf8View.initUnchecked(current_frame.contents[y]).iterator();
|
||||||
|
|
||||||
for (0..self.frame_width) |x| {
|
for (0..@intCast(self.dur_movie.columns)) |x| {
|
||||||
|
const cell_x = @as(i32, @intCast(x)) + self.start_pos[VEC_X];
|
||||||
|
|
||||||
const codepoint: u21 = iter.nextCodepoint().?;
|
const codepoint: u21 = iter.nextCodepoint().?;
|
||||||
const color_map = current_frame.colorMap[x][y];
|
const color_map = current_frame.colorMap[x][y];
|
||||||
|
|
||||||
|
|
@ -385,20 +530,20 @@ fn draw(self: *DurFile) void {
|
||||||
color_map_1 = durcolor_table_to_color16[color_map_1 + 1]; // Add 1, dur source stores it like this for some reason
|
color_map_1 = durcolor_table_to_color16[color_map_1 + 1]; // Add 1, dur source stores it like this for some reason
|
||||||
}
|
}
|
||||||
|
|
||||||
const fg_color = if (self.full_color) convert_256_to_rgb(color_map_0) else tb_color_16[color_map_0];
|
const fg_color = if (self.full_color) convert256ToRgb(color_map_0) else tb_color_16[color_map_0];
|
||||||
const bg_color = if (self.full_color) convert_256_to_rgb(color_map_1) else tb_color_16[color_map_1];
|
const bg_color = if (self.full_color) convert256ToRgb(color_map_1) else tb_color_16[color_map_1];
|
||||||
|
|
||||||
const cell = Cell{ .ch = @intCast(codepoint), .fg = fg_color, .bg = bg_color };
|
const cell = Cell{ .ch = @intCast(codepoint), .fg = fg_color, .bg = bg_color };
|
||||||
|
|
||||||
cell.put(x + self.x_offset, y + self.y_offset);
|
self.terminal_buffer.setCellBoundsChecked(cell_x, cell_y, cell) catch {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const time_current = std.time.milliTimestamp();
|
const time_current = std.Io.Timestamp.now(self.io, .real).toMilliseconds();
|
||||||
const delta_time = time_current - self.time_previous;
|
const delta_time = time_current - self.time_previous;
|
||||||
|
|
||||||
// Convert delay from sec to ms
|
// Convert delay from sec to ms
|
||||||
const delay_time: u32 = @intFromFloat(current_frame.delay * 1000);
|
const delay_time: u32 = @trunc(current_frame.delay * 1000);
|
||||||
if (delta_time > (self.frame_time + delay_time)) {
|
if (delta_time > (self.frame_time + delay_time)) {
|
||||||
self.time_previous = time_current;
|
self.time_previous = time_current;
|
||||||
|
|
||||||
|
|
@ -406,3 +551,15 @@ fn draw(self: *DurFile) void {
|
||||||
self.frames = (self.frames + 1) % frame_count;
|
self.frames = (self.frames + 1) % frame_count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update(self: *DurFile, _: *anyopaque) !void {
|
||||||
|
const time = try interop.getTimeOfDay();
|
||||||
|
|
||||||
|
if (self.timeout_sec > 0 and time.seconds - self.start_time.seconds > self.timeout_sec) {
|
||||||
|
self.animate.* = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculateTimeout(self: *DurFile, _: *anyopaque) !?usize {
|
||||||
|
return self.frame_delay;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Animation = @import("../tui/Animation.zig");
|
|
||||||
const Cell = @import("../tui/Cell.zig");
|
|
||||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const ly_ui = @import("ly-ui");
|
||||||
|
const Cell = ly_ui.Cell;
|
||||||
|
const TerminalBuffer = ly_ui.TerminalBuffer;
|
||||||
|
const Widget = ly_ui.Widget;
|
||||||
|
|
||||||
|
const ly_core = ly_ui.ly_core;
|
||||||
|
const interop = ly_core.interop;
|
||||||
|
const TimeOfDay = interop.TimeOfDay;
|
||||||
|
|
||||||
const GameOfLife = @This();
|
const GameOfLife = @This();
|
||||||
|
|
||||||
// Visual styles - using block characters like other animations
|
// Visual styles - using block characters like other animations
|
||||||
|
|
@ -16,6 +21,8 @@ const NEIGHBOR_DIRS = [_][2]i8{
|
||||||
.{ 1, 0 }, .{ 1, 1 },
|
.{ 1, 0 }, .{ 1, 1 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
instance: ?Widget = null,
|
||||||
|
start_time: TimeOfDay,
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
terminal_buffer: *TerminalBuffer,
|
terminal_buffer: *TerminalBuffer,
|
||||||
current_grid: []bool,
|
current_grid: []bool,
|
||||||
|
|
@ -26,11 +33,24 @@ fg_color: u32,
|
||||||
entropy_interval: usize,
|
entropy_interval: usize,
|
||||||
frame_delay: usize,
|
frame_delay: usize,
|
||||||
initial_density: f32,
|
initial_density: f32,
|
||||||
|
animate: *bool,
|
||||||
|
timeout_sec: u12,
|
||||||
|
animation_frame_delay: u16,
|
||||||
dead_cell: Cell,
|
dead_cell: Cell,
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg_color: u32, entropy_interval: usize, frame_delay: usize, initial_density: f32) !GameOfLife {
|
pub fn init(
|
||||||
|
allocator: Allocator,
|
||||||
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
fg_color: u32,
|
||||||
|
entropy_interval: usize,
|
||||||
|
frame_delay: usize,
|
||||||
|
initial_density: f32,
|
||||||
|
animate: *bool,
|
||||||
|
timeout_sec: u12,
|
||||||
|
animation_frame_delay: u16,
|
||||||
|
) !GameOfLife {
|
||||||
const width = terminal_buffer.width;
|
const width = terminal_buffer.width;
|
||||||
const height = terminal_buffer.height;
|
const height = terminal_buffer.height;
|
||||||
const grid_size = width * height;
|
const grid_size = width * height;
|
||||||
|
|
@ -39,6 +59,8 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg_color: u3
|
||||||
const next_grid = try allocator.alloc(bool, grid_size);
|
const next_grid = try allocator.alloc(bool, grid_size);
|
||||||
|
|
||||||
var game = GameOfLife{
|
var game = GameOfLife{
|
||||||
|
.instance = null,
|
||||||
|
.start_time = try interop.getTimeOfDay(),
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.terminal_buffer = terminal_buffer,
|
.terminal_buffer = terminal_buffer,
|
||||||
.current_grid = current_grid,
|
.current_grid = current_grid,
|
||||||
|
|
@ -49,6 +71,9 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg_color: u3
|
||||||
.entropy_interval = entropy_interval,
|
.entropy_interval = entropy_interval,
|
||||||
.frame_delay = frame_delay,
|
.frame_delay = frame_delay,
|
||||||
.initial_density = initial_density,
|
.initial_density = initial_density,
|
||||||
|
.animate = animate,
|
||||||
|
.timeout_sec = timeout_sec,
|
||||||
|
.animation_frame_delay = animation_frame_delay,
|
||||||
.dead_cell = .{ .ch = DEAD_CHAR, .fg = @intCast(TerminalBuffer.Color.DEFAULT), .bg = terminal_buffer.bg },
|
.dead_cell = .{ .ch = DEAD_CHAR, .fg = @intCast(TerminalBuffer.Color.DEFAULT), .bg = terminal_buffer.bg },
|
||||||
.width = width,
|
.width = width,
|
||||||
.height = height,
|
.height = height,
|
||||||
|
|
@ -60,8 +85,20 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg_color: u3
|
||||||
return game;
|
return game;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn animation(self: *GameOfLife) Animation {
|
pub fn widget(self: *GameOfLife) *Widget {
|
||||||
return Animation.init(self, deinit, realloc, draw);
|
if (self.instance) |*instance| return instance;
|
||||||
|
self.instance = Widget.init(
|
||||||
|
"GameOfLife",
|
||||||
|
null,
|
||||||
|
self,
|
||||||
|
deinit,
|
||||||
|
realloc,
|
||||||
|
draw,
|
||||||
|
update,
|
||||||
|
null,
|
||||||
|
calculateTimeout,
|
||||||
|
);
|
||||||
|
return &self.instance.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: *GameOfLife) void {
|
fn deinit(self: *GameOfLife) void {
|
||||||
|
|
@ -69,7 +106,7 @@ fn deinit(self: *GameOfLife) void {
|
||||||
self.allocator.free(self.next_grid);
|
self.allocator.free(self.next_grid);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realloc(self: *GameOfLife) anyerror!void {
|
fn realloc(self: *GameOfLife) !void {
|
||||||
const new_width = self.terminal_buffer.width;
|
const new_width = self.terminal_buffer.width;
|
||||||
const new_height = self.terminal_buffer.height;
|
const new_height = self.terminal_buffer.height;
|
||||||
const new_size = new_width * new_height;
|
const new_size = new_width * new_height;
|
||||||
|
|
@ -87,6 +124,8 @@ fn realloc(self: *GameOfLife) anyerror!void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(self: *GameOfLife) void {
|
fn draw(self: *GameOfLife) void {
|
||||||
|
if (!self.animate.*) return;
|
||||||
|
|
||||||
// Update game state at controlled frame rate
|
// Update game state at controlled frame rate
|
||||||
self.frame_counter += 1;
|
self.frame_counter += 1;
|
||||||
if (self.frame_counter >= self.frame_delay) {
|
if (self.frame_counter >= self.frame_delay) {
|
||||||
|
|
@ -107,11 +146,23 @@ fn draw(self: *GameOfLife) void {
|
||||||
const row_offset = y * self.width;
|
const row_offset = y * self.width;
|
||||||
for (0..self.width) |x| {
|
for (0..self.width) |x| {
|
||||||
const cell = if (self.current_grid[row_offset + x]) alive_cell else self.dead_cell;
|
const cell = if (self.current_grid[row_offset + x]) alive_cell else self.dead_cell;
|
||||||
cell.put(x, y);
|
cell.put(x, y) catch {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update(self: *GameOfLife, _: *anyopaque) !void {
|
||||||
|
const time = try interop.getTimeOfDay();
|
||||||
|
|
||||||
|
if (self.timeout_sec > 0 and time.seconds - self.start_time.seconds > self.timeout_sec) {
|
||||||
|
self.animate.* = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculateTimeout(self: *GameOfLife, _: *anyopaque) !?usize {
|
||||||
|
return self.animation_frame_delay;
|
||||||
|
}
|
||||||
|
|
||||||
fn updateGeneration(self: *GameOfLife) void {
|
fn updateGeneration(self: *GameOfLife) void {
|
||||||
// Conway's Game of Life rules with optimized neighbor counting
|
// Conway's Game of Life rules with optimized neighbor counting
|
||||||
for (0..self.height) |y| {
|
for (0..self.height) |y| {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Animation = @import("../tui/Animation.zig");
|
|
||||||
const Cell = @import("../tui/Cell.zig");
|
|
||||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Random = std.Random;
|
const Random = std.Random;
|
||||||
|
|
||||||
|
const ly_ui = @import("ly-ui");
|
||||||
|
const Cell = ly_ui.Cell;
|
||||||
|
const TerminalBuffer = ly_ui.TerminalBuffer;
|
||||||
|
const Widget = ly_ui.Widget;
|
||||||
|
|
||||||
|
const ly_core = ly_ui.ly_core;
|
||||||
|
const interop = ly_core.interop;
|
||||||
|
const TimeOfDay = interop.TimeOfDay;
|
||||||
|
|
||||||
pub const FRAME_DELAY: usize = 8;
|
pub const FRAME_DELAY: usize = 8;
|
||||||
|
|
||||||
// Characters change mid-scroll
|
// Characters change mid-scroll
|
||||||
|
|
@ -24,6 +29,8 @@ pub const Line = struct {
|
||||||
update: usize,
|
update: usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
instance: ?Widget = null,
|
||||||
|
start_time: TimeOfDay,
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
terminal_buffer: *TerminalBuffer,
|
terminal_buffer: *TerminalBuffer,
|
||||||
dots: []Dot,
|
dots: []Dot,
|
||||||
|
|
@ -34,15 +41,30 @@ fg: u32,
|
||||||
head_col: u32,
|
head_col: u32,
|
||||||
min_codepoint: u16,
|
min_codepoint: u16,
|
||||||
max_codepoint: u16,
|
max_codepoint: u16,
|
||||||
|
animate: *bool,
|
||||||
|
timeout_sec: u12,
|
||||||
|
frame_delay: u16,
|
||||||
default_cell: Cell,
|
default_cell: Cell,
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg: u32, head_col: u32, min_codepoint: u16, max_codepoint: u16) !Matrix {
|
pub fn init(
|
||||||
|
allocator: Allocator,
|
||||||
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
fg: u32,
|
||||||
|
head_col: u32,
|
||||||
|
min_codepoint: u16,
|
||||||
|
max_codepoint: u16,
|
||||||
|
animate: *bool,
|
||||||
|
timeout_sec: u12,
|
||||||
|
frame_delay: u16,
|
||||||
|
) !Matrix {
|
||||||
const dots = try allocator.alloc(Dot, terminal_buffer.width * (terminal_buffer.height + 1));
|
const dots = try allocator.alloc(Dot, terminal_buffer.width * (terminal_buffer.height + 1));
|
||||||
const lines = try allocator.alloc(Line, terminal_buffer.width);
|
const lines = try allocator.alloc(Line, terminal_buffer.width);
|
||||||
|
|
||||||
initBuffers(dots, lines, terminal_buffer.width, terminal_buffer.height, terminal_buffer.random);
|
initBuffers(dots, lines, terminal_buffer.width, terminal_buffer.height, terminal_buffer.random);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
|
.instance = null,
|
||||||
|
.start_time = try interop.getTimeOfDay(),
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.terminal_buffer = terminal_buffer,
|
.terminal_buffer = terminal_buffer,
|
||||||
.dots = dots,
|
.dots = dots,
|
||||||
|
|
@ -53,12 +75,27 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg: u32, hea
|
||||||
.head_col = head_col,
|
.head_col = head_col,
|
||||||
.min_codepoint = min_codepoint,
|
.min_codepoint = min_codepoint,
|
||||||
.max_codepoint = max_codepoint - min_codepoint,
|
.max_codepoint = max_codepoint - min_codepoint,
|
||||||
|
.animate = animate,
|
||||||
|
.timeout_sec = timeout_sec,
|
||||||
|
.frame_delay = frame_delay,
|
||||||
.default_cell = .{ .ch = ' ', .fg = fg, .bg = terminal_buffer.bg },
|
.default_cell = .{ .ch = ' ', .fg = fg, .bg = terminal_buffer.bg },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn animation(self: *Matrix) Animation {
|
pub fn widget(self: *Matrix) *Widget {
|
||||||
return Animation.init(self, deinit, realloc, draw);
|
if (self.instance) |*instance| return instance;
|
||||||
|
self.instance = Widget.init(
|
||||||
|
"Matrix",
|
||||||
|
null,
|
||||||
|
self,
|
||||||
|
deinit,
|
||||||
|
realloc,
|
||||||
|
draw,
|
||||||
|
update,
|
||||||
|
null,
|
||||||
|
calculateTimeout,
|
||||||
|
);
|
||||||
|
return &self.instance.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: *Matrix) void {
|
fn deinit(self: *Matrix) void {
|
||||||
|
|
@ -66,7 +103,7 @@ fn deinit(self: *Matrix) void {
|
||||||
self.allocator.free(self.lines);
|
self.allocator.free(self.lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realloc(self: *Matrix) anyerror!void {
|
fn realloc(self: *Matrix) !void {
|
||||||
const dots = try self.allocator.realloc(self.dots, self.terminal_buffer.width * (self.terminal_buffer.height + 1));
|
const dots = try self.allocator.realloc(self.dots, self.terminal_buffer.width * (self.terminal_buffer.height + 1));
|
||||||
const lines = try self.allocator.realloc(self.lines, self.terminal_buffer.width);
|
const lines = try self.allocator.realloc(self.lines, self.terminal_buffer.width);
|
||||||
|
|
||||||
|
|
@ -77,6 +114,8 @@ fn realloc(self: *Matrix) anyerror!void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(self: *Matrix) void {
|
fn draw(self: *Matrix) void {
|
||||||
|
if (!self.animate.*) return;
|
||||||
|
|
||||||
const buf_height = self.terminal_buffer.height;
|
const buf_height = self.terminal_buffer.height;
|
||||||
const buf_width = self.terminal_buffer.width;
|
const buf_width = self.terminal_buffer.width;
|
||||||
self.count += 1;
|
self.count += 1;
|
||||||
|
|
@ -86,17 +125,17 @@ fn draw(self: *Matrix) void {
|
||||||
self.count = 0;
|
self.count = 0;
|
||||||
|
|
||||||
var x: usize = 0;
|
var x: usize = 0;
|
||||||
while (x < self.terminal_buffer.width) : (x += 2) {
|
while (x < buf_width) : (x += 2) {
|
||||||
var tail: usize = 0;
|
var tail: usize = 0;
|
||||||
var line = &self.lines[x];
|
var line = &self.lines[x];
|
||||||
if (self.frame <= line.update) continue;
|
if (self.frame <= line.update) continue;
|
||||||
|
|
||||||
if (self.dots[x].value == null and self.dots[self.terminal_buffer.width + x].value == ' ') {
|
if (self.dots[x].value == null and self.dots[buf_width + x].value == ' ') {
|
||||||
if (line.space > 0) {
|
if (line.space > 0) {
|
||||||
line.space -= 1;
|
line.space -= 1;
|
||||||
} else {
|
} else {
|
||||||
const randint = self.terminal_buffer.random.int(u16);
|
const randint = self.terminal_buffer.random.int(u16);
|
||||||
const h = self.terminal_buffer.height;
|
const h = buf_height;
|
||||||
line.length = @mod(randint, h - 3) + 3;
|
line.length = @mod(randint, h - 3) + 3;
|
||||||
self.dots[x].value = @mod(randint, self.max_codepoint) + self.min_codepoint;
|
self.dots[x].value = @mod(randint, self.max_codepoint) + self.min_codepoint;
|
||||||
line.space = @mod(randint, h + 1);
|
line.space = @mod(randint, h + 1);
|
||||||
|
|
@ -153,7 +192,7 @@ fn draw(self: *Matrix) void {
|
||||||
var x: usize = 0;
|
var x: usize = 0;
|
||||||
while (x < buf_width) : (x += 2) {
|
while (x < buf_width) : (x += 2) {
|
||||||
var y: usize = 1;
|
var y: usize = 1;
|
||||||
while (y <= self.terminal_buffer.height) : (y += 1) {
|
while (y <= buf_height) : (y += 1) {
|
||||||
const dot = self.dots[buf_width * y + x];
|
const dot = self.dots[buf_width * y + x];
|
||||||
const cell = if (dot.value == null or dot.value == ' ') self.default_cell else Cell{
|
const cell = if (dot.value == null or dot.value == ' ') self.default_cell else Cell{
|
||||||
.ch = @intCast(dot.value.?),
|
.ch = @intCast(dot.value.?),
|
||||||
|
|
@ -161,13 +200,25 @@ fn draw(self: *Matrix) void {
|
||||||
.bg = self.terminal_buffer.bg,
|
.bg = self.terminal_buffer.bg,
|
||||||
};
|
};
|
||||||
|
|
||||||
cell.put(x, y - 1);
|
cell.put(x, y - 1) catch {};
|
||||||
// Fill background in between columns
|
// Fill background in between columns
|
||||||
self.default_cell.put(x + 1, y - 1);
|
self.default_cell.put(x + 1, y - 1) catch {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update(self: *Matrix, _: *anyopaque) !void {
|
||||||
|
const time = try interop.getTimeOfDay();
|
||||||
|
|
||||||
|
if (self.timeout_sec > 0 and time.seconds - self.start_time.seconds > self.timeout_sec) {
|
||||||
|
self.animate.* = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculateTimeout(self: *Matrix, _: *anyopaque) !?usize {
|
||||||
|
return self.frame_delay;
|
||||||
|
}
|
||||||
|
|
||||||
fn initBuffers(dots: []Dot, lines: []Line, width: usize, height: usize, random: Random) void {
|
fn initBuffers(dots: []Dot, lines: []Line, width: usize, height: usize, random: Random) void {
|
||||||
var y: usize = 0;
|
var y: usize = 0;
|
||||||
while (y <= height) : (y += 1) {
|
while (y <= height) : (y += 1) {
|
||||||
|
|
|
||||||
308
src/auth.zig
308
src/auth.zig
|
|
@ -1,16 +1,17 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const build_options = @import("build_options");
|
|
||||||
const builtin = @import("builtin");
|
|
||||||
const enums = @import("enums.zig");
|
|
||||||
const Environment = @import("Environment.zig");
|
|
||||||
const interop = @import("interop.zig");
|
|
||||||
const SharedError = @import("SharedError.zig");
|
|
||||||
const LogFile = @import("LogFile.zig");
|
|
||||||
|
|
||||||
const Md5 = std.crypto.hash.Md5;
|
const Md5 = std.crypto.hash.Md5;
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const build_options = @import("build_options");
|
||||||
|
|
||||||
|
const ly_core = @import("ly-ui").ly_core;
|
||||||
|
const interop = ly_core.interop;
|
||||||
|
const SharedError = ly_core.SharedError;
|
||||||
|
const LogFile = ly_core.LogFile;
|
||||||
const utmp = interop.utmp;
|
const utmp = interop.utmp;
|
||||||
const Utmp = utmp.utmpx;
|
const Utmp = utmp.utmpx;
|
||||||
|
|
||||||
|
const Environment = @import("Environment.zig");
|
||||||
|
|
||||||
pub const AuthOptions = struct {
|
pub const AuthOptions = struct {
|
||||||
tty: u8,
|
tty: u8,
|
||||||
service_name: [:0]const u8,
|
service_name: [:0]const u8,
|
||||||
|
|
@ -20,20 +21,22 @@ pub const AuthOptions = struct {
|
||||||
setup_cmd: []const u8,
|
setup_cmd: []const u8,
|
||||||
login_cmd: ?[]const u8,
|
login_cmd: ?[]const u8,
|
||||||
x_cmd: []const u8,
|
x_cmd: []const u8,
|
||||||
|
x_vt: ?u8,
|
||||||
session_pid: std.posix.pid_t,
|
session_pid: std.posix.pid_t,
|
||||||
|
use_kmscon_vt: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
var xorg_pid: std.posix.pid_t = 0;
|
var xorg_pid: std.posix.pid_t = 0;
|
||||||
pub fn xorgSignalHandler(i: c_int) callconv(.c) void {
|
pub fn xorgSignalHandler(sig: std.posix.SIG) callconv(.c) void {
|
||||||
if (xorg_pid > 0) _ = std.c.kill(xorg_pid, i);
|
if (xorg_pid > 0) _ = std.c.kill(xorg_pid, sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
var child_pid: std.posix.pid_t = 0;
|
var child_pid: std.posix.pid_t = 0;
|
||||||
pub fn sessionSignalHandler(i: c_int) callconv(.c) void {
|
pub fn sessionSignalHandler(sig: std.posix.SIG) callconv(.c) void {
|
||||||
if (child_pid > 0) _ = std.c.kill(child_pid, i);
|
if (child_pid > 0) _ = std.c.kill(child_pid, sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authenticate(allocator: std.mem.Allocator, log_file: *LogFile, options: AuthOptions, current_environment: Environment, login: []const u8, password: []const u8) !void {
|
pub fn authenticate(allocator: std.mem.Allocator, io: std.Io, log_file: *LogFile, options: AuthOptions, current_environment: Environment, login: []const u8, password: []const u8) !void {
|
||||||
var tty_buffer: [3]u8 = undefined;
|
var tty_buffer: [3]u8 = undefined;
|
||||||
const tty_str = try std.fmt.bufPrint(&tty_buffer, "{d}", .{options.tty});
|
const tty_str = try std.fmt.bufPrint(&tty_buffer, "{d}", .{options.tty});
|
||||||
|
|
||||||
|
|
@ -41,9 +44,11 @@ pub fn authenticate(allocator: std.mem.Allocator, log_file: *LogFile, options: A
|
||||||
const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{options.tty});
|
const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{options.tty});
|
||||||
|
|
||||||
// Set the XDG environment variables
|
// Set the XDG environment variables
|
||||||
|
try log_file.info(io, "auth/env", "setting xdg environment variables", .{});
|
||||||
try setXdgEnv(allocator, tty_str, current_environment);
|
try setXdgEnv(allocator, tty_str, current_environment);
|
||||||
|
|
||||||
// Open the PAM session
|
// Open the PAM session
|
||||||
|
try log_file.info(io, "auth/pam", "encoding credentials", .{});
|
||||||
const login_z = try allocator.dupeZ(u8, login);
|
const login_z = try allocator.dupeZ(u8, login);
|
||||||
defer allocator.free(login_z);
|
defer allocator.free(login_z);
|
||||||
|
|
||||||
|
|
@ -58,37 +63,36 @@ pub fn authenticate(allocator: std.mem.Allocator, log_file: *LogFile, options: A
|
||||||
};
|
};
|
||||||
var handle: ?*interop.pam.pam_handle = undefined;
|
var handle: ?*interop.pam.pam_handle = undefined;
|
||||||
|
|
||||||
var log_writer = &log_file.file_writer.interface;
|
try log_file.info(io, "auth/pam", "starting session", .{});
|
||||||
|
|
||||||
try log_writer.writeAll("[pam] starting session\n");
|
|
||||||
var status = interop.pam.pam_start(options.service_name, null, &conv, &handle);
|
var status = interop.pam.pam_start(options.service_name, null, &conv, &handle);
|
||||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||||
defer _ = interop.pam.pam_end(handle, status);
|
defer _ = interop.pam.pam_end(handle, status);
|
||||||
|
|
||||||
// Set PAM_TTY as the current TTY. This is required in case it isn't being set by another PAM module
|
// Set PAM_TTY as the current TTY. This is required in case it isn't being set by another PAM module
|
||||||
try log_writer.writeAll("[pam] setting tty\n");
|
try log_file.info(io, "auth/pam", "setting tty", .{});
|
||||||
status = interop.pam.pam_set_item(handle, interop.pam.PAM_TTY, pam_tty_str.ptr);
|
status = interop.pam.pam_set_item(handle, interop.pam.PAM_TTY, pam_tty_str.ptr);
|
||||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||||
|
|
||||||
// Do the PAM routine
|
// Do the PAM routine
|
||||||
try log_writer.writeAll("[pam] authenticating\n");
|
try log_file.info(io, "auth/pam", "authenticating", .{});
|
||||||
status = interop.pam.pam_authenticate(handle, 0);
|
status = interop.pam.pam_authenticate(handle, 0);
|
||||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||||
|
|
||||||
try log_writer.writeAll("[pam] validating account\n");
|
try log_file.info(io, "auth/pam", "validating account", .{});
|
||||||
status = interop.pam.pam_acct_mgmt(handle, 0);
|
status = interop.pam.pam_acct_mgmt(handle, 0);
|
||||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||||
|
|
||||||
try log_writer.writeAll("[pam] setting credentials\n");
|
try log_file.info(io, "auth/pam", "setting credentials", .{});
|
||||||
status = interop.pam.pam_setcred(handle, interop.pam.PAM_ESTABLISH_CRED);
|
status = interop.pam.pam_setcred(handle, interop.pam.PAM_ESTABLISH_CRED);
|
||||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||||
defer status = interop.pam.pam_setcred(handle, interop.pam.PAM_DELETE_CRED);
|
defer status = interop.pam.pam_setcred(handle, interop.pam.PAM_DELETE_CRED);
|
||||||
|
|
||||||
try log_writer.writeAll("[pam] opening session\n");
|
try log_file.info(io, "auth/pam", "opening session", .{});
|
||||||
status = interop.pam.pam_open_session(handle, 0);
|
status = interop.pam.pam_open_session(handle, 0);
|
||||||
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
|
||||||
defer status = interop.pam.pam_close_session(handle, 0);
|
defer status = interop.pam.pam_close_session(handle, 0);
|
||||||
|
|
||||||
|
try log_file.info(io, "auth/passwd", "getting struct", .{});
|
||||||
var user_entry: interop.UsernameEntry = undefined;
|
var user_entry: interop.UsernameEntry = undefined;
|
||||||
{
|
{
|
||||||
defer interop.closePasswordDatabase();
|
defer interop.closePasswordDatabase();
|
||||||
|
|
@ -98,28 +102,27 @@ pub fn authenticate(allocator: std.mem.Allocator, log_file: *LogFile, options: A
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set user shell if it hasn't already been set
|
// Set user shell if it hasn't already been set
|
||||||
|
try log_file.info(io, "auth/passwd", "setting user shell", .{});
|
||||||
if (user_entry.shell == null) interop.setUserShell(&user_entry);
|
if (user_entry.shell == null) interop.setUserShell(&user_entry);
|
||||||
|
|
||||||
var shared_err = try SharedError.init();
|
var shared_err = try SharedError.init(null, null);
|
||||||
defer shared_err.deinit();
|
defer shared_err.deinit();
|
||||||
|
|
||||||
log_file.deinit();
|
log_file.deinit(io);
|
||||||
|
|
||||||
child_pid = try std.posix.fork();
|
child_pid = std.posix.system.fork();
|
||||||
if (child_pid == 0) {
|
if (child_pid == 0) {
|
||||||
try log_file.reinit();
|
try log_file.reinit(io);
|
||||||
log_writer = &log_file.file_writer.interface;
|
try log_file.info(io, "auth/sys", "starting session", .{});
|
||||||
|
|
||||||
try log_writer.writeAll("starting session\n");
|
startSession(log_file, allocator, io, options, tty_str, user_entry, handle, current_environment) catch |e| {
|
||||||
|
|
||||||
startSession(log_file, allocator, options, tty_str, user_entry, handle, current_environment) catch |e| {
|
|
||||||
shared_err.writeError(e);
|
shared_err.writeError(e);
|
||||||
|
|
||||||
log_file.deinit();
|
log_file.deinit(io);
|
||||||
std.process.exit(1);
|
std.process.exit(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
log_file.deinit();
|
log_file.deinit(io);
|
||||||
std.process.exit(0);
|
std.process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,7 +132,8 @@ pub fn authenticate(allocator: std.mem.Allocator, log_file: *LogFile, options: A
|
||||||
// If an error occurs here, we can send SIGTERM to the session
|
// If an error occurs here, we can send SIGTERM to the session
|
||||||
errdefer cleanup: {
|
errdefer cleanup: {
|
||||||
std.posix.kill(child_pid, std.posix.SIG.TERM) catch break :cleanup;
|
std.posix.kill(child_pid, std.posix.SIG.TERM) catch break :cleanup;
|
||||||
_ = std.posix.waitpid(child_pid, 0);
|
var child_status: c_int = undefined;
|
||||||
|
_ = std.posix.system.waitpid(child_pid, &child_status, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we receive SIGTERM, forward it to child_pid
|
// If we receive SIGTERM, forward it to child_pid
|
||||||
|
|
@ -140,13 +144,15 @@ pub fn authenticate(allocator: std.mem.Allocator, log_file: *LogFile, options: A
|
||||||
};
|
};
|
||||||
std.posix.sigaction(std.posix.SIG.TERM, &act, null);
|
std.posix.sigaction(std.posix.SIG.TERM, &act, null);
|
||||||
|
|
||||||
try addUtmpEntry(&entry, user_entry.username.?, child_pid);
|
try addUtmpEntry(io, &entry, user_entry.username.?, child_pid);
|
||||||
}
|
}
|
||||||
// Wait for the session to stop
|
// Wait for the session to stop
|
||||||
_ = std.posix.waitpid(child_pid, 0);
|
var child_status: c_int = undefined;
|
||||||
|
_ = std.posix.system.waitpid(child_pid, &child_status, 0);
|
||||||
|
|
||||||
try log_file.reinit();
|
try log_file.reinit(io);
|
||||||
|
|
||||||
|
try log_file.info(io, "auth/utmp", "removing utmp entry", .{});
|
||||||
removeUtmpEntry(&entry);
|
removeUtmpEntry(&entry);
|
||||||
|
|
||||||
if (shared_err.readError()) |err| return err;
|
if (shared_err.readError()) |err| return err;
|
||||||
|
|
@ -155,6 +161,7 @@ pub fn authenticate(allocator: std.mem.Allocator, log_file: *LogFile, options: A
|
||||||
fn startSession(
|
fn startSession(
|
||||||
log_file: *LogFile,
|
log_file: *LogFile,
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
io: std.Io,
|
||||||
options: AuthOptions,
|
options: AuthOptions,
|
||||||
tty_str: []u8,
|
tty_str: []u8,
|
||||||
user_entry: interop.UsernameEntry,
|
user_entry: interop.UsernameEntry,
|
||||||
|
|
@ -162,34 +169,49 @@ fn startSession(
|
||||||
current_environment: Environment,
|
current_environment: Environment,
|
||||||
) !void {
|
) !void {
|
||||||
// Set the user's GID & PID
|
// Set the user's GID & PID
|
||||||
|
try log_file.info(io, "auth/passwd", "setting user context", .{});
|
||||||
try interop.setUserContext(allocator, user_entry);
|
try interop.setUserContext(allocator, user_entry);
|
||||||
|
|
||||||
// Set up the environment
|
// Set up the environment
|
||||||
|
try log_file.info(io, "auth/env", "setting environment variables", .{});
|
||||||
try initEnv(allocator, user_entry, options.path);
|
try initEnv(allocator, user_entry, options.path);
|
||||||
|
|
||||||
// Reset the XDG environment variables
|
// Reset the XDG environment variables
|
||||||
|
try log_file.info(io, "auth/env", "resetting xdg environment variables", .{});
|
||||||
try setXdgEnv(allocator, tty_str, current_environment);
|
try setXdgEnv(allocator, tty_str, current_environment);
|
||||||
|
try setXdgRuntimeDir(allocator);
|
||||||
|
|
||||||
// Set the PAM variables
|
// Set the PAM variables
|
||||||
const pam_env_vars: ?[*:null]?[*:0]u8 = interop.pam.pam_getenvlist(handle);
|
const pam_env_vars: ?[*:null]?[*:0]u8 = interop.pam.pam_getenvlist(handle);
|
||||||
if (pam_env_vars == null) return error.GetEnvListFailed;
|
if (pam_env_vars == null) return error.GetEnvListFailed;
|
||||||
|
|
||||||
const env_list = std.mem.span(pam_env_vars.?);
|
const env_list = std.mem.span(pam_env_vars.?);
|
||||||
for (env_list) |env_var| try interop.putEnvironmentVariable(env_var);
|
for (env_list) |env_var| {
|
||||||
|
if (env_var == null) continue;
|
||||||
|
try log_file.info(io, "auth/env", "setting pam environment variable: {s}", .{std.mem.span(env_var.?)});
|
||||||
|
try interop.putEnvironmentVariable(env_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
const home_z = try allocator.dupeZ(u8, user_entry.home.?);
|
||||||
|
defer allocator.free(home_z);
|
||||||
|
|
||||||
// Change to the user's home directory
|
// Change to the user's home directory
|
||||||
std.posix.chdir(user_entry.home.?) catch return error.ChangeDirectoryFailed;
|
try log_file.info(io, "auth/sys", "changing cwd to user home", .{});
|
||||||
|
if (std.posix.system.chdir(home_z.ptr) < 0) return error.ChangeDirectoryFailed;
|
||||||
|
|
||||||
// Signal to the session process to give up control on the TTY
|
// Signal to the session process to give up control on the TTY
|
||||||
std.posix.kill(options.session_pid, std.posix.SIG.CHLD) catch return error.TtyControlTransferFailed;
|
try log_file.info(io, "auth/sys", "releasing tty", .{});
|
||||||
|
std.posix.kill(options.session_pid, std.posix.SIG.INT) catch return error.TtyControlTransferFailed;
|
||||||
|
|
||||||
// Execute what the user requested
|
// Execute what the user requested
|
||||||
switch (current_environment.display_server) {
|
switch (current_environment.display_server) {
|
||||||
.wayland, .shell, .custom => try executeCmd(log_file, allocator, user_entry.shell.?, options, current_environment.is_terminal, current_environment.cmd),
|
.wayland, .shell, .custom => try executeCmd(log_file, allocator, io, user_entry.shell.?, options, current_environment.is_terminal, current_environment.cmd),
|
||||||
.xinitrc, .x11 => if (build_options.enable_x11_support) {
|
.xinitrc, .x11 => if (build_options.enable_x11_support) {
|
||||||
var vt_buf: [5]u8 = undefined;
|
var vt_buf: [5]u8 = undefined;
|
||||||
const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{options.tty});
|
const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{options.x_vt orelse options.tty});
|
||||||
try executeX11Cmd(log_file, allocator, user_entry.shell.?, user_entry.home.?, options, current_environment.cmd orelse "", vt);
|
|
||||||
|
try log_file.info(io, "auth/x11", "setting vt to {s}", .{vt});
|
||||||
|
try executeX11Cmd(log_file, allocator, io, user_entry.shell.?, user_entry.home.?, options, current_environment.cmd orelse "", vt);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -217,18 +239,6 @@ fn setXdgEnv(allocator: std.mem.Allocator, tty_str: []u8, environment: Environme
|
||||||
.custom => if (environment.is_terminal) "tty" else "unspecified",
|
.custom => if (environment.is_terminal) "tty" else "unspecified",
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
// The "/run/user/%d" directory is not available on FreeBSD. It is much
|
|
||||||
// better to stick to the defaults and let applications using
|
|
||||||
// XDG_RUNTIME_DIR to fall back to directories inside user's home
|
|
||||||
// directory.
|
|
||||||
if (builtin.os.tag != .freebsd) {
|
|
||||||
const uid = std.posix.getuid();
|
|
||||||
var uid_buffer: [32]u8 = undefined; // No UID can be larger than this
|
|
||||||
const uid_str = try std.fmt.bufPrint(&uid_buffer, "/run/user/{d}", .{uid});
|
|
||||||
|
|
||||||
try interop.setEnvironmentVariable(allocator, "XDG_RUNTIME_DIR", uid_str, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (environment.xdg_desktop_names) |xdg_desktop_names| try interop.setEnvironmentVariable(allocator, "XDG_CURRENT_DESKTOP", xdg_desktop_names, false);
|
if (environment.xdg_desktop_names) |xdg_desktop_names| try interop.setEnvironmentVariable(allocator, "XDG_CURRENT_DESKTOP", xdg_desktop_names, false);
|
||||||
try interop.setEnvironmentVariable(allocator, "XDG_SESSION_CLASS", "user", false);
|
try interop.setEnvironmentVariable(allocator, "XDG_SESSION_CLASS", "user", false);
|
||||||
try interop.setEnvironmentVariable(allocator, "XDG_SESSION_ID", "1", false);
|
try interop.setEnvironmentVariable(allocator, "XDG_SESSION_ID", "1", false);
|
||||||
|
|
@ -237,6 +247,20 @@ fn setXdgEnv(allocator: std.mem.Allocator, tty_str: []u8, environment: Environme
|
||||||
try interop.setEnvironmentVariable(allocator, "XDG_VTNR", tty_str, false);
|
try interop.setEnvironmentVariable(allocator, "XDG_VTNR", tty_str, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn setXdgRuntimeDir(allocator: std.mem.Allocator) !void {
|
||||||
|
// The "/run/user/%d" directory is not available on FreeBSD. It is much
|
||||||
|
// better to stick to the defaults and let applications using
|
||||||
|
// XDG_RUNTIME_DIR to fall back to directories inside user's home
|
||||||
|
// directory.
|
||||||
|
if (builtin.os.tag != .freebsd) {
|
||||||
|
const uid = std.posix.system.getuid();
|
||||||
|
var uid_buffer: [32]u8 = undefined; // No UID can be larger than this
|
||||||
|
const uid_str = try std.fmt.bufPrint(&uid_buffer, "/run/user/{d}", .{uid});
|
||||||
|
|
||||||
|
try interop.setEnvironmentVariable(allocator, "XDG_RUNTIME_DIR", uid_str, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn loginConv(
|
fn loginConv(
|
||||||
num_msg: c_int,
|
num_msg: c_int,
|
||||||
msg: ?[*]?*const interop.pam.pam_message,
|
msg: ?[*]?*const interop.pam.pam_message,
|
||||||
|
|
@ -299,20 +323,20 @@ fn getFreeDisplay() !u8 {
|
||||||
var buf: [15]u8 = undefined;
|
var buf: [15]u8 = undefined;
|
||||||
var i: u8 = 0;
|
var i: u8 = 0;
|
||||||
while (i < 200) : (i += 1) {
|
while (i < 200) : (i += 1) {
|
||||||
const xlock = try std.fmt.bufPrint(&buf, "/tmp/.X{d}-lock", .{i});
|
const xlock = try std.fmt.bufPrintZ(&buf, "/tmp/.X{d}-lock", .{i});
|
||||||
std.posix.access(xlock, std.posix.F_OK) catch break;
|
if (interop.isError(std.posix.system.access(xlock.ptr, std.posix.F_OK))) break;
|
||||||
}
|
}
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getXPid(display_num: u8) !i32 {
|
fn getXPid(io: std.Io, display_num: u8) !i32 {
|
||||||
var buf: [15]u8 = undefined;
|
var buf: [15]u8 = undefined;
|
||||||
const file_name = try std.fmt.bufPrint(&buf, "/tmp/.X{d}-lock", .{display_num});
|
const file_name = try std.fmt.bufPrint(&buf, "/tmp/.X{d}-lock", .{display_num});
|
||||||
const file = try std.fs.openFileAbsolute(file_name, .{});
|
const file = try std.Io.Dir.openFileAbsolute(io, file_name, .{});
|
||||||
defer file.close();
|
defer file.close(io);
|
||||||
|
|
||||||
var file_buffer: [32]u8 = undefined;
|
var file_buffer: [32]u8 = undefined;
|
||||||
var file_reader = file.reader(&file_buffer);
|
var file_reader = file.reader(io, &file_buffer);
|
||||||
var reader = &file_reader.interface;
|
var reader = &file_reader.interface;
|
||||||
|
|
||||||
var buffer: [20]u8 = undefined;
|
var buffer: [20]u8 = undefined;
|
||||||
|
|
@ -322,41 +346,41 @@ fn getXPid(display_num: u8) !i32 {
|
||||||
return std.fmt.parseInt(i32, std.mem.trim(u8, buffer[0..written], " "), 10);
|
return std.fmt.parseInt(i32, std.mem.trim(u8, buffer[0..written], " "), 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn createXauthFile(pwd: []const u8, buffer: []u8) ![]const u8 {
|
fn createXauthFile(log_file: *LogFile, io: std.Io, pwd: []const u8, buffer: []u8) ![]const u8 {
|
||||||
var xauth_buf: [100]u8 = undefined;
|
var xauth_buf: [100]u8 = undefined;
|
||||||
var xauth_dir: []const u8 = undefined;
|
var xauth_dir: []const u8 = undefined;
|
||||||
const xdg_rt_dir = std.posix.getenv("XDG_RUNTIME_DIR");
|
const xdg_rt_dir = std.posix.system.getenv("XDG_RUNTIME_DIR");
|
||||||
var xauth_file: []const u8 = "lyxauth";
|
var xauth_file: []const u8 = "lyxauth";
|
||||||
|
|
||||||
if (xdg_rt_dir == null) no_rt_dir: {
|
if (xdg_rt_dir == null) no_rt_dir: {
|
||||||
const xdg_cfg_home = std.posix.getenv("XDG_CONFIG_HOME");
|
const xdg_cfg_home = std.posix.system.getenv("XDG_CONFIG_HOME");
|
||||||
if (xdg_cfg_home == null) no_cfg_home: {
|
if (xdg_cfg_home == null) no_cfg_home: {
|
||||||
xauth_dir = try std.fmt.bufPrint(&xauth_buf, "{s}/.config", .{pwd});
|
xauth_dir = try std.fmt.bufPrint(&xauth_buf, "{s}/.config", .{pwd});
|
||||||
|
|
||||||
var dir = std.fs.cwd().openDir(xauth_dir, .{}) catch {
|
var dir = std.Io.Dir.cwd().openDir(io, xauth_dir, .{}) catch {
|
||||||
// xauth_dir isn't a directory
|
// xauth_dir isn't a directory
|
||||||
xauth_dir = pwd;
|
xauth_dir = pwd;
|
||||||
xauth_file = ".lyxauth";
|
xauth_file = ".lyxauth";
|
||||||
break :no_cfg_home;
|
break :no_cfg_home;
|
||||||
};
|
};
|
||||||
dir.close();
|
dir.close(io);
|
||||||
|
|
||||||
// xauth_dir is a directory, use it to store Xauthority
|
// xauth_dir is a directory, use it to store Xauthority
|
||||||
xauth_dir = try std.fmt.bufPrint(&xauth_buf, "{s}/.config/ly", .{pwd});
|
xauth_dir = try std.fmt.bufPrint(&xauth_buf, "{s}/.config/ly", .{pwd});
|
||||||
} else {
|
} else {
|
||||||
xauth_dir = try std.fmt.bufPrint(&xauth_buf, "{s}/ly", .{xdg_cfg_home.?});
|
xauth_dir = try std.fmt.bufPrint(&xauth_buf, "{s}/ly", .{std.mem.span(xdg_cfg_home.?)});
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = std.fs.cwd().openFile(xauth_dir, .{}) catch break :no_rt_dir;
|
const file = std.Io.Dir.cwd().openFile(io, xauth_dir, .{}) catch break :no_rt_dir;
|
||||||
file.close();
|
file.close(io);
|
||||||
|
|
||||||
// xauth_dir is a file, create the parent directory
|
// xauth_dir is a file, create the parent directory
|
||||||
std.posix.mkdir(xauth_dir, 777) catch {
|
std.Io.Dir.createDirAbsolute(io, xauth_dir, .fromMode(777)) catch {
|
||||||
xauth_dir = pwd;
|
xauth_dir = pwd;
|
||||||
xauth_file = ".lyxauth";
|
xauth_file = ".lyxauth";
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
xauth_dir = xdg_rt_dir.?;
|
xauth_dir = std.mem.span(xdg_rt_dir.?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trim trailing slashes
|
// Trim trailing slashes
|
||||||
|
|
@ -366,17 +390,19 @@ fn createXauthFile(pwd: []const u8, buffer: []u8) ![]const u8 {
|
||||||
|
|
||||||
const xauthority: []u8 = try std.fmt.bufPrint(buffer, "{s}/{s}", .{ trimmed_xauth_dir, xauth_file });
|
const xauthority: []u8 = try std.fmt.bufPrint(buffer, "{s}/{s}", .{ trimmed_xauth_dir, xauth_file });
|
||||||
|
|
||||||
std.fs.makeDirAbsolute(trimmed_xauth_dir) catch {};
|
std.Io.Dir.cwd().createDirPath(io, trimmed_xauth_dir) catch {};
|
||||||
|
|
||||||
const file = try std.fs.createFileAbsolute(xauthority, .{});
|
try log_file.info(io, "auth/x11", "creating xauth file: {s}", .{xauthority});
|
||||||
file.close();
|
|
||||||
|
const file = try std.Io.Dir.createFileAbsolute(io, xauthority, .{});
|
||||||
|
file.close(io);
|
||||||
|
|
||||||
return xauthority;
|
return xauthority;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mcookie() [Md5.digest_length * 2]u8 {
|
fn mcookie(io: std.Io) [Md5.digest_length * 2]u8 {
|
||||||
var buf: [4096]u8 = undefined;
|
var buf: [4096]u8 = undefined;
|
||||||
std.crypto.random.bytes(&buf);
|
io.random(&buf);
|
||||||
|
|
||||||
var out: [Md5.digest_length]u8 = undefined;
|
var out: [Md5.digest_length]u8 = undefined;
|
||||||
Md5.hash(&buf, &out, .{});
|
Md5.hash(&buf, &out, .{});
|
||||||
|
|
@ -384,79 +410,100 @@ fn mcookie() [Md5.digest_length * 2]u8 {
|
||||||
return std.fmt.bytesToHex(&out, .lower);
|
return std.fmt.bytesToHex(&out, .lower);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xauth(log_file: *LogFile, allocator: std.mem.Allocator, display_name: []u8, shell: [*:0]const u8, home: []const u8, xauth_buffer: []u8, options: AuthOptions) !void {
|
fn xauth(log_file: *LogFile, allocator: std.mem.Allocator, io: std.Io, display_name: []u8, shell: [*:0]const u8, home: []const u8, xauth_buffer: []u8, options: AuthOptions) ![]const u8 {
|
||||||
const xauthority = try createXauthFile(home, xauth_buffer);
|
const xauthority = try createXauthFile(log_file, io, home, xauth_buffer);
|
||||||
try interop.setEnvironmentVariable(allocator, "XAUTHORITY", xauthority, true);
|
try interop.setEnvironmentVariable(allocator, "XAUTHORITY", xauthority, true);
|
||||||
try interop.setEnvironmentVariable(allocator, "DISPLAY", display_name, true);
|
try interop.setEnvironmentVariable(allocator, "DISPLAY", display_name, true);
|
||||||
|
|
||||||
const magic_cookie = mcookie();
|
const magic_cookie = mcookie(io);
|
||||||
|
|
||||||
const pid = try std.posix.fork();
|
log_file.deinit(io);
|
||||||
|
|
||||||
|
const pid = std.posix.system.fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
|
try log_file.reinit(io);
|
||||||
|
|
||||||
var cmd_buffer: [1024]u8 = undefined;
|
var cmd_buffer: [1024]u8 = undefined;
|
||||||
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . {s}", .{ options.xauth_cmd, display_name, magic_cookie }) catch std.process.exit(1);
|
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . {s}", .{ options.xauth_cmd, display_name, magic_cookie }) catch std.process.exit(1);
|
||||||
|
|
||||||
|
try log_file.info(io, "auth/x11", "executing: {s} -c {s}", .{ shell, cmd_str });
|
||||||
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
|
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
|
||||||
std.posix.execveZ(shell, &args, std.c.environ) catch {};
|
_ = std.posix.system.execve(shell, &args, std.c.environ);
|
||||||
|
|
||||||
|
log_file.deinit(io);
|
||||||
std.process.exit(1);
|
std.process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const status = std.posix.waitpid(pid, 0);
|
var status: c_int = undefined;
|
||||||
if (status.status != 0) {
|
const result = std.posix.system.waitpid(pid, &status, 0);
|
||||||
try log_file.file_writer.interface.print("xauth command failed with status {d}\n", .{status.status});
|
|
||||||
|
try log_file.reinit(io);
|
||||||
|
if (interop.isError(result) or status != 0) {
|
||||||
|
try log_file.err(
|
||||||
|
io,
|
||||||
|
"auth/x11",
|
||||||
|
"xauth command failed with status: {d}",
|
||||||
|
.{status},
|
||||||
|
);
|
||||||
return error.XauthFailed;
|
return error.XauthFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return xauthority;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn executeX11Cmd(log_file: *LogFile, allocator: std.mem.Allocator, shell: []const u8, home: []const u8, options: AuthOptions, desktop_cmd: []const u8, vt: []const u8) !void {
|
fn executeX11Cmd(log_file: *LogFile, allocator: std.mem.Allocator, io: std.Io, shell: []const u8, home: []const u8, options: AuthOptions, desktop_cmd: []const u8, vt: []const u8) !void {
|
||||||
var log_writer = &log_file.file_writer.interface;
|
|
||||||
var xauth_buffer: [256]u8 = undefined;
|
var xauth_buffer: [256]u8 = undefined;
|
||||||
|
|
||||||
try log_writer.writeAll("[x11] getting free display\n");
|
try log_file.info(io, "auth/x11", "getting free display", .{});
|
||||||
const display_num = try getFreeDisplay();
|
const display_num = try getFreeDisplay();
|
||||||
var buf: [4]u8 = undefined;
|
var buf: [4]u8 = undefined;
|
||||||
const display_name = try std.fmt.bufPrint(&buf, ":{d}", .{display_num});
|
const display_name = try std.fmt.bufPrint(&buf, ":{d}", .{display_num});
|
||||||
|
try log_file.info(io, "auth/x11", "got free display: {d}", .{display_num});
|
||||||
|
|
||||||
const shell_z = try allocator.dupeZ(u8, shell);
|
const shell_z = try allocator.dupeZ(u8, shell);
|
||||||
defer allocator.free(shell_z);
|
defer allocator.free(shell_z);
|
||||||
|
|
||||||
try log_writer.writeAll("[x11] creating xauth file\n");
|
try log_file.info(io, "auth/x11", "creating xauth file", .{});
|
||||||
try xauth(log_file, allocator, display_name, shell_z, home, &xauth_buffer, options);
|
const xauthority = try xauth(log_file, allocator, io, display_name, shell_z, home, &xauth_buffer, options);
|
||||||
|
|
||||||
try log_writer.writeAll("[x11] starting x server\n");
|
try log_file.info(io, "auth/x11", "starting x server", .{});
|
||||||
const pid = try std.posix.fork();
|
const pid = std.posix.system.fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
var cmd_buffer: [1024]u8 = undefined;
|
var cmd_buffer: [1024]u8 = undefined;
|
||||||
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.x_cmd, display_name, vt }) catch std.process.exit(1);
|
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s} -auth {s}", .{ options.x_cmd, display_name, vt, xauthority }) catch std.process.exit(1);
|
||||||
|
try log_file.info(io, "auth/x11", "executing: {s} -c {s} -auth {s}", .{ shell, cmd_str, xauthority });
|
||||||
|
|
||||||
const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str };
|
const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str };
|
||||||
std.posix.execveZ(shell_z, &args, std.c.environ) catch {};
|
_ = std.posix.system.execve(shell_z, &args, std.c.environ);
|
||||||
std.process.exit(1);
|
std.process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var ok: c_int = undefined;
|
try log_file.info(io, "auth/x11", "waiting for xcb connection", .{});
|
||||||
|
var ok: c_int = -1;
|
||||||
var xcb: ?*interop.xcb.xcb_connection_t = null;
|
var xcb: ?*interop.xcb.xcb_connection_t = null;
|
||||||
while (ok != 0) {
|
while (ok != 0) {
|
||||||
xcb = interop.xcb.xcb_connect(null, null);
|
xcb = interop.xcb.xcb_connect(null, null);
|
||||||
ok = interop.xcb.xcb_connection_has_error(xcb);
|
ok = interop.xcb.xcb_connection_has_error(xcb);
|
||||||
std.posix.kill(pid, 0) catch |e| {
|
std.posix.kill(pid, @enumFromInt(0)) catch |e| {
|
||||||
if (e == error.ProcessNotFound and ok != 0) return error.XcbConnectionFailed;
|
if (e == error.ProcessNotFound and ok != 0) return error.XcbConnectionFailed;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// X Server detaches from the process.
|
// X Server detaches from the process.
|
||||||
// PID can be fetched from /tmp/X{d}.lock
|
// PID can be fetched from /tmp/X{d}.lock
|
||||||
try log_writer.writeAll("[x11] getting x server pid\n");
|
try log_file.info(io, "auth/x11", "getting x server pid", .{});
|
||||||
const x_pid = try getXPid(display_num);
|
const x_pid = try getXPid(io, display_num);
|
||||||
|
try log_file.info(io, "auth/x11", "got x server pid: {d}", .{x_pid});
|
||||||
|
|
||||||
try log_writer.writeAll("[x11] launching environment\n");
|
try log_file.info(io, "auth/x11", "launching environment", .{});
|
||||||
xorg_pid = try std.posix.fork();
|
xorg_pid = std.posix.system.fork();
|
||||||
if (xorg_pid == 0) {
|
if (xorg_pid == 0) {
|
||||||
var cmd_buffer: [1024]u8 = undefined;
|
var cmd_buffer: [1024]u8 = undefined;
|
||||||
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", desktop_cmd }) catch std.process.exit(1);
|
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s} {s}", .{ if (options.use_kmscon_vt) "kmscon-launch-gui" else "", options.setup_cmd, options.login_cmd orelse "", desktop_cmd }) catch std.process.exit(1);
|
||||||
|
try log_file.info(io, "auth/x11", "executing: {s} -c {s}", .{ shell, cmd_str });
|
||||||
|
|
||||||
const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str };
|
const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str };
|
||||||
std.posix.execveZ(shell_z, &args, std.c.environ) catch {};
|
_ = std.posix.system.execve(shell_z, &args, std.c.environ);
|
||||||
std.process.exit(1);
|
std.process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -468,60 +515,83 @@ fn executeX11Cmd(log_file: *LogFile, allocator: std.mem.Allocator, shell: []cons
|
||||||
};
|
};
|
||||||
std.posix.sigaction(std.posix.SIG.TERM, &act, null);
|
std.posix.sigaction(std.posix.SIG.TERM, &act, null);
|
||||||
|
|
||||||
_ = std.posix.waitpid(xorg_pid, 0);
|
var xorg_status: c_int = undefined;
|
||||||
|
_ = std.posix.system.waitpid(xorg_pid, &xorg_status, 0);
|
||||||
|
|
||||||
|
try log_file.info(io, "auth/x11", "disconnecting xcb", .{});
|
||||||
interop.xcb.xcb_disconnect(xcb);
|
interop.xcb.xcb_disconnect(xcb);
|
||||||
|
|
||||||
// TODO: Find a more robust way to ensure that X has been terminated (pidfds?)
|
// TODO: Find a more robust way to ensure that X has been terminated (pidfds?)
|
||||||
std.posix.kill(x_pid, std.posix.SIG.TERM) catch {};
|
std.posix.kill(x_pid, std.posix.SIG.TERM) catch {};
|
||||||
std.Thread.sleep(std.time.ns_per_s * 1); // Wait 1 second before sending SIGKILL
|
io.sleep(.fromSeconds(1), .real) catch {}; // Wait 1 second before sending SIGKILL
|
||||||
std.posix.kill(x_pid, std.posix.SIG.KILL) catch return;
|
std.posix.kill(x_pid, std.posix.SIG.KILL) catch return;
|
||||||
|
|
||||||
_ = std.posix.waitpid(x_pid, 0);
|
var x_status: c_int = undefined;
|
||||||
|
_ = std.posix.system.waitpid(x_pid, &x_status, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn executeCmd(global_log_file: *LogFile, allocator: std.mem.Allocator, shell: []const u8, options: AuthOptions, is_terminal: bool, exec_cmd: ?[]const u8) !void {
|
fn executeCmd(global_log_file: *LogFile, allocator: std.mem.Allocator, io: std.Io, shell: []const u8, options: AuthOptions, is_terminal: bool, exec_cmd: ?[]const u8) !void {
|
||||||
var maybe_log_file: ?std.fs.File = null;
|
try global_log_file.info(io, "auth/sys", "launching wayland/shell/custom session", .{});
|
||||||
if (!is_terminal) {
|
|
||||||
|
var maybe_log_file: ?std.Io.File = null;
|
||||||
|
if (!is_terminal) redirect_streams: {
|
||||||
|
if (options.use_kmscon_vt) {
|
||||||
|
try global_log_file.err(io, "auth/sys", "cannot redirect stdio & stderr with kmscon", .{});
|
||||||
|
break :redirect_streams;
|
||||||
|
}
|
||||||
|
|
||||||
// For custom desktop entries, the "Terminal" value here determines if
|
// For custom desktop entries, the "Terminal" value here determines if
|
||||||
// we redirect standard output & error or not. That is, we redirect only
|
// we redirect standard output & error or not. That is, we redirect only
|
||||||
// if it's equal to false (so if it's not running in a TTY).
|
// if it's equal to false (so if it's not running in a TTY).
|
||||||
if (options.session_log) |log_path| {
|
if (options.session_log) |log_path| {
|
||||||
maybe_log_file = try redirectStandardStreams(global_log_file, log_path, true);
|
try global_log_file.info(io, "auth/sys", "setting up stdio & stderr redirection", .{});
|
||||||
|
maybe_log_file = try redirectStandardStreams(global_log_file, io, log_path, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defer if (maybe_log_file) |log_file| log_file.close();
|
defer if (maybe_log_file) |log_file| log_file.close(io);
|
||||||
|
|
||||||
const shell_z = try allocator.dupeZ(u8, shell);
|
const shell_z = try allocator.dupeZ(u8, shell);
|
||||||
defer allocator.free(shell_z);
|
defer allocator.free(shell_z);
|
||||||
|
|
||||||
var cmd_buffer: [1024]u8 = undefined;
|
var cmd_buffer: [1024]u8 = undefined;
|
||||||
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", exec_cmd orelse shell });
|
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s} {s}", .{ if (!is_terminal and options.use_kmscon_vt) "kmscon-launch-gui" else "", options.setup_cmd, options.login_cmd orelse "", exec_cmd orelse shell });
|
||||||
|
|
||||||
|
try global_log_file.info(io, "auth/sys", "executing: {s} -c {s}", .{ shell, cmd_str });
|
||||||
const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str };
|
const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str };
|
||||||
return std.posix.execveZ(shell_z, &args, std.c.environ);
|
_ = std.posix.system.execve(shell_z, &args, std.c.environ);
|
||||||
|
return error.CmdExecveFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn redirectStandardStreams(global_log_file: *LogFile, session_log: []const u8, create: bool) !std.fs.File {
|
fn redirectStandardStreams(global_log_file: *LogFile, io: std.Io, session_log: []const u8, create: bool) !std.Io.File {
|
||||||
const log_file = if (create) (std.fs.cwd().createFile(session_log, .{ .mode = 0o666 }) catch |err| {
|
create_session_log_dir: {
|
||||||
try global_log_file.file_writer.interface.print("failed to create new session log file: {s}\n", .{@errorName(err)});
|
const session_log_dir = std.Io.Dir.path.dirname(session_log) orelse break :create_session_log_dir;
|
||||||
|
std.Io.Dir.cwd().createDirPath(io, session_log_dir) catch |err| {
|
||||||
|
try global_log_file.err(io, "auth/sys", "failed to create session log file directory: {s}", .{@errorName(err)});
|
||||||
return err;
|
return err;
|
||||||
}) else (std.fs.cwd().openFile(session_log, .{ .mode = .read_write }) catch |err| {
|
};
|
||||||
try global_log_file.file_writer.interface.print("failed to open existing session log file: {s}\n", .{@errorName(err)});
|
}
|
||||||
|
|
||||||
|
const log_file = if (create) (std.Io.Dir.cwd().createFile(io, session_log, .{ .permissions = .fromMode(0o666) }) catch |err| {
|
||||||
|
try global_log_file.err(io, "auth/sys", "failed to create new session log file: {s}", .{@errorName(err)});
|
||||||
|
return err;
|
||||||
|
}) else (std.Io.Dir.cwd().openFile(io, session_log, .{ .mode = .read_write }) catch |err| {
|
||||||
|
try global_log_file.err(io, "auth/sys", "failed to open existing session log file: {s}", .{@errorName(err)});
|
||||||
return err;
|
return err;
|
||||||
});
|
});
|
||||||
|
|
||||||
try std.posix.dup2(std.posix.STDOUT_FILENO, std.posix.STDERR_FILENO);
|
if (interop.isError(std.posix.system.dup2(std.posix.STDOUT_FILENO, std.posix.STDERR_FILENO))) return error.StdoutDup2Failed;
|
||||||
try std.posix.dup2(log_file.handle, std.posix.STDOUT_FILENO);
|
if (interop.isError(std.posix.system.dup2(log_file.handle, std.posix.STDOUT_FILENO))) return error.LogFileDup2Failed;
|
||||||
|
|
||||||
return log_file;
|
return log_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addUtmpEntry(entry: *Utmp, username: []const u8, pid: c_int) !void {
|
fn addUtmpEntry(io: std.Io, entry: *Utmp, username: []const u8, pid: c_int) !void {
|
||||||
entry.ut_type = utmp.USER_PROCESS;
|
entry.ut_type = utmp.USER_PROCESS;
|
||||||
entry.ut_pid = pid;
|
entry.ut_pid = pid;
|
||||||
|
|
||||||
var buf: [std.fs.max_path_bytes]u8 = undefined;
|
var buf: [std.Io.Dir.max_path_bytes]u8 = undefined;
|
||||||
const tty_path = try std.os.getFdPath(std.posix.STDIN_FILENO, &buf);
|
const length = try std.Io.File.stdin().realPath(io, &buf);
|
||||||
|
const tty_path = buf[0..length];
|
||||||
|
|
||||||
// Get the TTY name (i.e. without the /dev/ prefix)
|
// Get the TTY name (i.e. without the /dev/ prefix)
|
||||||
var ttyname_buf: [@sizeOf(@TypeOf(entry.ut_line))]u8 = undefined;
|
var ttyname_buf: [@sizeOf(@TypeOf(entry.ut_line))]u8 = undefined;
|
||||||
|
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const interop = @import("interop.zig");
|
|
||||||
const enums = @import("enums.zig");
|
|
||||||
const Lang = @import("bigclock/Lang.zig");
|
|
||||||
const en = @import("bigclock/en.zig");
|
|
||||||
const fa = @import("bigclock/fa.zig");
|
|
||||||
const Cell = @import("tui/Cell.zig");
|
|
||||||
|
|
||||||
const Bigclock = enums.Bigclock;
|
|
||||||
pub const WIDTH = Lang.WIDTH;
|
|
||||||
pub const HEIGHT = Lang.HEIGHT;
|
|
||||||
pub const SIZE = Lang.SIZE;
|
|
||||||
|
|
||||||
pub fn clockCell(animate: bool, char: u8, fg: u32, bg: u32, bigclock: Bigclock) ![SIZE]Cell {
|
|
||||||
var cells: [SIZE]Cell = undefined;
|
|
||||||
|
|
||||||
const time = try interop.getTimeOfDay();
|
|
||||||
const clock_chars = toBigNumber(if (animate and char == ':' and @divTrunc(time.microseconds, 500000) != 0) ' ' else char, bigclock);
|
|
||||||
for (0..cells.len) |i| cells[i] = Cell.init(clock_chars[i], fg, bg);
|
|
||||||
|
|
||||||
return cells;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn alphaBlit(x: usize, y: usize, tb_width: usize, tb_height: usize, cells: [SIZE]Cell) void {
|
|
||||||
if (x + WIDTH >= tb_width or y + HEIGHT >= tb_height) return;
|
|
||||||
|
|
||||||
for (0..HEIGHT) |yy| {
|
|
||||||
for (0..WIDTH) |xx| {
|
|
||||||
const cell = cells[yy * WIDTH + xx];
|
|
||||||
cell.put(x + xx, y + yy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn toBigNumber(char: u8, bigclock: Bigclock) [SIZE]u21 {
|
|
||||||
const locale_chars = switch (bigclock) {
|
|
||||||
.fa => fa.locale_chars,
|
|
||||||
.en => en.locale_chars,
|
|
||||||
.none => unreachable,
|
|
||||||
};
|
|
||||||
return switch (char) {
|
|
||||||
'0' => locale_chars.ZERO,
|
|
||||||
'1' => locale_chars.ONE,
|
|
||||||
'2' => locale_chars.TWO,
|
|
||||||
'3' => locale_chars.THREE,
|
|
||||||
'4' => locale_chars.FOUR,
|
|
||||||
'5' => locale_chars.FIVE,
|
|
||||||
'6' => locale_chars.SIX,
|
|
||||||
'7' => locale_chars.SEVEN,
|
|
||||||
'8' => locale_chars.EIGHT,
|
|
||||||
'9' => locale_chars.NINE,
|
|
||||||
'p', 'P' => locale_chars.P,
|
|
||||||
'a', 'A' => locale_chars.A,
|
|
||||||
'm', 'M' => locale_chars.M,
|
|
||||||
':' => locale_chars.S,
|
|
||||||
else => locale_chars.E,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
const interop = @import("../interop.zig");
|
|
||||||
|
|
||||||
pub const WIDTH = 5;
|
|
||||||
pub const HEIGHT = 5;
|
|
||||||
pub const SIZE = WIDTH * HEIGHT;
|
|
||||||
|
|
||||||
pub const X: u32 = if (interop.supportsUnicode()) 0x2593 else '#';
|
|
||||||
pub const O: u32 = 0;
|
|
||||||
|
|
||||||
// zig fmt: off
|
|
||||||
pub const LocaleChars = struct {
|
|
||||||
ZERO: [SIZE]u21,
|
|
||||||
ONE: [SIZE]u21,
|
|
||||||
TWO: [SIZE]u21,
|
|
||||||
THREE: [SIZE]u21,
|
|
||||||
FOUR: [SIZE]u21,
|
|
||||||
FIVE: [SIZE]u21,
|
|
||||||
SIX: [SIZE]u21,
|
|
||||||
SEVEN: [SIZE]u21,
|
|
||||||
EIGHT: [SIZE]u21,
|
|
||||||
NINE: [SIZE]u21,
|
|
||||||
S: [SIZE]u21,
|
|
||||||
E: [SIZE]u21,
|
|
||||||
P: [SIZE]u21,
|
|
||||||
A: [SIZE]u21,
|
|
||||||
M: [SIZE]u21,
|
|
||||||
};
|
|
||||||
// zig fmt: on
|
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const ly_ui = @import("ly-ui");
|
||||||
|
const keyboard = ly_ui.keyboard;
|
||||||
|
const TerminalBuffer = ly_ui.TerminalBuffer;
|
||||||
|
const Widget = ly_ui.Widget;
|
||||||
|
const CyclableLabel = ly_ui.CyclableLabel;
|
||||||
|
|
||||||
|
const MessageLabel = CyclableLabel(Message, Message);
|
||||||
|
|
||||||
|
const InfoLine = @This();
|
||||||
|
|
||||||
|
const Message = struct {
|
||||||
|
width: usize,
|
||||||
|
text: []const u8,
|
||||||
|
bg: u32,
|
||||||
|
fg: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
instance: ?Widget = null,
|
||||||
|
label: *MessageLabel,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
allocator: Allocator,
|
||||||
|
io: std.Io,
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
width: usize,
|
||||||
|
arrow_fg: u32,
|
||||||
|
arrow_bg: u32,
|
||||||
|
) !InfoLine {
|
||||||
|
return .{
|
||||||
|
.instance = null,
|
||||||
|
.label = try MessageLabel.init(
|
||||||
|
allocator,
|
||||||
|
io,
|
||||||
|
buffer,
|
||||||
|
drawItem,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
width,
|
||||||
|
true,
|
||||||
|
arrow_fg,
|
||||||
|
arrow_bg,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *InfoLine) void {
|
||||||
|
self.label.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn widget(self: *InfoLine) *Widget {
|
||||||
|
if (self.instance) |*instance| return instance;
|
||||||
|
self.instance = Widget.init(
|
||||||
|
"InfoLine",
|
||||||
|
self.label.keybinds,
|
||||||
|
self,
|
||||||
|
deinit,
|
||||||
|
null,
|
||||||
|
draw,
|
||||||
|
null,
|
||||||
|
handle,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
return &self.instance.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addMessage(self: *InfoLine, text: []const u8, bg: u32, fg: u32) !void {
|
||||||
|
if (text.len == 0) return;
|
||||||
|
|
||||||
|
try self.label.addItem(.{
|
||||||
|
.width = TerminalBuffer.strWidth(text),
|
||||||
|
.text = text,
|
||||||
|
.bg = bg,
|
||||||
|
.fg = fg,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clearRendered(self: InfoLine, allocator: Allocator) !void {
|
||||||
|
// Draw over the area
|
||||||
|
const spaces = try allocator.alloc(u8, self.label.width - 2);
|
||||||
|
defer allocator.free(spaces);
|
||||||
|
|
||||||
|
@memset(spaces, ' ');
|
||||||
|
|
||||||
|
try TerminalBuffer.drawText(
|
||||||
|
spaces,
|
||||||
|
self.label.component_pos.x + 2,
|
||||||
|
self.label.component_pos.y,
|
||||||
|
TerminalBuffer.Color.DEFAULT,
|
||||||
|
TerminalBuffer.Color.DEFAULT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(self: *InfoLine) void {
|
||||||
|
self.label.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(self: *InfoLine, maybe_key: ?keyboard.Key) !void {
|
||||||
|
try self.label.handle(maybe_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drawItem(label: *MessageLabel, message: Message, x: usize, y: usize, width: usize) void {
|
||||||
|
if (message.width == 0) return;
|
||||||
|
|
||||||
|
const x_offset = if (label.text_in_center and width >= message.width) (width - message.width) / 2 else 0;
|
||||||
|
|
||||||
|
label.cursor = message.width + x_offset;
|
||||||
|
TerminalBuffer.drawConfinedText(
|
||||||
|
message.text,
|
||||||
|
x + x_offset,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
message.fg,
|
||||||
|
message.bg,
|
||||||
|
) catch {};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const ly_ui = @import("ly-ui");
|
||||||
|
const keyboard = ly_ui.keyboard;
|
||||||
|
const TerminalBuffer = ly_ui.TerminalBuffer;
|
||||||
|
const Widget = ly_ui.Widget;
|
||||||
|
const CyclableLabel = ly_ui.CyclableLabel;
|
||||||
|
|
||||||
|
const UserList = @import("UserList.zig");
|
||||||
|
const Environment = @import("../Environment.zig");
|
||||||
|
|
||||||
|
const Env = struct {
|
||||||
|
environment: Environment,
|
||||||
|
index: usize,
|
||||||
|
};
|
||||||
|
const EnvironmentLabel = CyclableLabel(Env, *UserList);
|
||||||
|
|
||||||
|
const Session = @This();
|
||||||
|
|
||||||
|
instance: ?Widget = null,
|
||||||
|
label: *EnvironmentLabel,
|
||||||
|
user_list: *UserList,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
allocator: Allocator,
|
||||||
|
io: std.Io,
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
user_list: *UserList,
|
||||||
|
width: usize,
|
||||||
|
text_in_center: bool,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
) !Session {
|
||||||
|
return .{
|
||||||
|
.instance = null,
|
||||||
|
.label = try EnvironmentLabel.init(
|
||||||
|
allocator,
|
||||||
|
io,
|
||||||
|
buffer,
|
||||||
|
drawItem,
|
||||||
|
sessionChanged,
|
||||||
|
user_list,
|
||||||
|
width,
|
||||||
|
text_in_center,
|
||||||
|
fg,
|
||||||
|
bg,
|
||||||
|
),
|
||||||
|
.user_list = user_list,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Session) void {
|
||||||
|
for (self.label.list.items) |*env| {
|
||||||
|
if (env.environment.entry_ini) |*entry_ini| entry_ini.deinit();
|
||||||
|
self.label.allocator.free(env.environment.file_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.label.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn widget(self: *Session) *Widget {
|
||||||
|
if (self.instance) |*instance| return instance;
|
||||||
|
self.instance = Widget.init(
|
||||||
|
"Session",
|
||||||
|
self.label.keybinds,
|
||||||
|
self,
|
||||||
|
deinit,
|
||||||
|
null,
|
||||||
|
draw,
|
||||||
|
null,
|
||||||
|
handle,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
return &self.instance.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addEnvironment(self: *Session, environment: Environment) !void {
|
||||||
|
const env = Env{ .environment = environment, .index = self.label.list.items.len };
|
||||||
|
|
||||||
|
try self.label.addItem(env);
|
||||||
|
addedSession(env, self.user_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(self: *Session) void {
|
||||||
|
self.label.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(self: *Session, maybe_key: ?keyboard.Key) !void {
|
||||||
|
try self.label.handle(maybe_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addedSession(env: Env, user_list: *UserList) void {
|
||||||
|
const user = user_list.label.list.items[user_list.label.current];
|
||||||
|
if (!user.first_run) return;
|
||||||
|
|
||||||
|
user.session_index.* = env.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sessionChanged(env: Env, maybe_user_list: ?*UserList) void {
|
||||||
|
if (maybe_user_list) |user_list| {
|
||||||
|
user_list.label.list.items[user_list.label.current].session_index.* = env.index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drawItem(label: *EnvironmentLabel, env: Env, x: usize, y: usize, width: usize) void {
|
||||||
|
if (width < 3) return;
|
||||||
|
|
||||||
|
const length = @min(TerminalBuffer.strWidth(env.environment.name), width - 3);
|
||||||
|
if (length == 0) return;
|
||||||
|
|
||||||
|
const x_offset = if (label.text_in_center and width >= length) (width - length) / 2 else 0;
|
||||||
|
|
||||||
|
label.cursor = length + x_offset;
|
||||||
|
TerminalBuffer.drawConfinedText(
|
||||||
|
env.environment.name,
|
||||||
|
x + x_offset,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
label.fg,
|
||||||
|
label.bg,
|
||||||
|
) catch {};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const ly_ui = @import("ly-ui");
|
||||||
|
const keyboard = ly_ui.keyboard;
|
||||||
|
const TerminalBuffer = ly_ui.TerminalBuffer;
|
||||||
|
const Widget = ly_ui.Widget;
|
||||||
|
const CyclableLabel = ly_ui.CyclableLabel;
|
||||||
|
|
||||||
|
const Session = @import("Session.zig");
|
||||||
|
const SavedUsers = @import("../config/SavedUsers.zig");
|
||||||
|
|
||||||
|
const StringList = std.ArrayListUnmanaged([]const u8);
|
||||||
|
pub const User = struct {
|
||||||
|
name: []const u8,
|
||||||
|
session_index: *usize,
|
||||||
|
allocated_index: bool,
|
||||||
|
first_run: bool,
|
||||||
|
};
|
||||||
|
const UserLabel = CyclableLabel(User, *Session);
|
||||||
|
|
||||||
|
const UserList = @This();
|
||||||
|
|
||||||
|
instance: ?Widget = null,
|
||||||
|
label: *UserLabel,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
allocator: Allocator,
|
||||||
|
io: std.Io,
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
usernames: StringList,
|
||||||
|
saved_users: *SavedUsers,
|
||||||
|
session: *Session,
|
||||||
|
width: usize,
|
||||||
|
text_in_center: bool,
|
||||||
|
fg: u32,
|
||||||
|
bg: u32,
|
||||||
|
) !UserList {
|
||||||
|
var user_list = UserList{
|
||||||
|
.instance = null,
|
||||||
|
.label = try UserLabel.init(
|
||||||
|
allocator,
|
||||||
|
io,
|
||||||
|
buffer,
|
||||||
|
drawItem,
|
||||||
|
usernameChanged,
|
||||||
|
session,
|
||||||
|
width,
|
||||||
|
text_in_center,
|
||||||
|
fg,
|
||||||
|
bg,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (usernames.items) |username| {
|
||||||
|
if (username.len == 0) continue;
|
||||||
|
|
||||||
|
var maybe_session_index: ?*usize = null;
|
||||||
|
var first_run = true;
|
||||||
|
for (saved_users.user_list.items) |*saved_user| {
|
||||||
|
if (std.mem.eql(u8, username, saved_user.username)) {
|
||||||
|
maybe_session_index = &saved_user.session_index;
|
||||||
|
first_run = saved_user.first_run;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var allocated_index = false;
|
||||||
|
if (maybe_session_index == null) {
|
||||||
|
maybe_session_index = try allocator.create(usize);
|
||||||
|
maybe_session_index.?.* = 0;
|
||||||
|
allocated_index = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try user_list.label.addItem(.{
|
||||||
|
.name = username,
|
||||||
|
.session_index = maybe_session_index.?,
|
||||||
|
.allocated_index = allocated_index,
|
||||||
|
.first_run = first_run,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return user_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *UserList) void {
|
||||||
|
for (self.label.list.items) |user| {
|
||||||
|
if (user.allocated_index) {
|
||||||
|
self.label.allocator.destroy(user.session_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.label.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn widget(self: *UserList) *Widget {
|
||||||
|
if (self.instance) |*instance| return instance;
|
||||||
|
self.instance = Widget.init(
|
||||||
|
"UserList",
|
||||||
|
self.label.keybinds,
|
||||||
|
self,
|
||||||
|
deinit,
|
||||||
|
null,
|
||||||
|
draw,
|
||||||
|
null,
|
||||||
|
handle,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
return &self.instance.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getCurrentUsername(self: UserList) []const u8 {
|
||||||
|
return self.label.list.items[self.label.current].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(self: *UserList) void {
|
||||||
|
self.label.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(self: *UserList, maybe_key: ?keyboard.Key) !void {
|
||||||
|
try self.label.handle(maybe_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usernameChanged(user: User, maybe_session: ?*Session) void {
|
||||||
|
if (maybe_session) |session| {
|
||||||
|
session.label.current = @min(user.session_index.*, session.label.list.items.len - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drawItem(label: *UserLabel, user: User, x: usize, y: usize, width: usize) void {
|
||||||
|
if (width < 3) return;
|
||||||
|
|
||||||
|
const length = @min(TerminalBuffer.strWidth(user.name), width - 3);
|
||||||
|
if (length == 0) return;
|
||||||
|
|
||||||
|
const x_offset = if (label.text_in_center and width >= length) (width - length) / 2 else 0;
|
||||||
|
|
||||||
|
label.cursor = length + x_offset;
|
||||||
|
TerminalBuffer.drawConfinedText(
|
||||||
|
user.name,
|
||||||
|
x + x_offset,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
label.fg,
|
||||||
|
label.bg,
|
||||||
|
) catch {};
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
const build_options = @import("build_options");
|
const build_options = @import("build_options");
|
||||||
const enums = @import("../enums.zig");
|
|
||||||
|
|
||||||
|
const enums = @import("../enums.zig");
|
||||||
const Animation = enums.Animation;
|
const Animation = enums.Animation;
|
||||||
const Input = enums.Input;
|
const Input = enums.Input;
|
||||||
const ViMode = enums.ViMode;
|
const ViMode = enums.ViMode;
|
||||||
const Bigclock = enums.Bigclock;
|
const Bigclock = enums.Bigclock;
|
||||||
|
const DurOffsetAlignment = enums.DurOffsetAlignment;
|
||||||
|
|
||||||
allow_empty_password: bool = true,
|
allow_empty_password: bool = true,
|
||||||
animation: Animation = .none,
|
animation: Animation = .none,
|
||||||
|
animation_frame_delay: u16 = 5,
|
||||||
animation_timeout_sec: u12 = 0,
|
animation_timeout_sec: u12 = 0,
|
||||||
asterisk: ?u32 = '*',
|
asterisk: ?u32 = '*',
|
||||||
auth_fails: u64 = 10,
|
auth_fails: u64 = 10,
|
||||||
|
|
@ -21,6 +23,8 @@ bigclock_12hr: bool = false,
|
||||||
bigclock_seconds: bool = false,
|
bigclock_seconds: bool = false,
|
||||||
blank_box: bool = true,
|
blank_box: bool = true,
|
||||||
border_fg: u32 = 0x00FFFFFF,
|
border_fg: u32 = 0x00FFFFFF,
|
||||||
|
box_position_h: f32 = 0.5,
|
||||||
|
box_position_v: f32 = 0.4,
|
||||||
box_title: ?[]const u8 = null,
|
box_title: ?[]const u8 = null,
|
||||||
brightness_down_cmd: [:0]const u8 = build_options.prefix_directory ++ "/bin/brightnessctl -q -n s 10%-",
|
brightness_down_cmd: [:0]const u8 = build_options.prefix_directory ++ "/bin/brightnessctl -q -n s 10%-",
|
||||||
brightness_down_key: ?[]const u8 = "F5",
|
brightness_down_key: ?[]const u8 = "F5",
|
||||||
|
|
@ -35,6 +39,7 @@ cmatrix_max_codepoint: u16 = 0x7B,
|
||||||
colormix_col1: u32 = 0x00FF0000,
|
colormix_col1: u32 = 0x00FF0000,
|
||||||
colormix_col2: u32 = 0x000000FF,
|
colormix_col2: u32 = 0x000000FF,
|
||||||
colormix_col3: u32 = 0x20000000,
|
colormix_col3: u32 = 0x20000000,
|
||||||
|
custom_bind_width: ?u32 = null,
|
||||||
custom_sessions: []const u8 = build_options.config_directory ++ "/ly/custom-sessions",
|
custom_sessions: []const u8 = build_options.config_directory ++ "/ly/custom-sessions",
|
||||||
default_input: Input = .login,
|
default_input: Input = .login,
|
||||||
doom_fire_height: u8 = 6,
|
doom_fire_height: u8 = 6,
|
||||||
|
|
@ -43,8 +48,9 @@ doom_top_color: u32 = 0x00FF0000,
|
||||||
doom_middle_color: u32 = 0x00FFFF00,
|
doom_middle_color: u32 = 0x00FFFF00,
|
||||||
doom_bottom_color: u32 = 0x00FFFFFF,
|
doom_bottom_color: u32 = 0x00FFFFFF,
|
||||||
dur_file_path: []const u8 = build_options.config_directory ++ "/ly/example.dur",
|
dur_file_path: []const u8 = build_options.config_directory ++ "/ly/example.dur",
|
||||||
dur_x_offset: u32 = 0,
|
dur_offset_alignment: DurOffsetAlignment = .center,
|
||||||
dur_y_offset: u32 = 0,
|
dur_x_offset: i32 = 0,
|
||||||
|
dur_y_offset: i32 = 0,
|
||||||
edge_margin: u8 = 0,
|
edge_margin: u8 = 0,
|
||||||
error_bg: u32 = 0x00000000,
|
error_bg: u32 = 0x00000000,
|
||||||
error_fg: u32 = 0x01FF0000,
|
error_fg: u32 = 0x01FF0000,
|
||||||
|
|
@ -68,10 +74,9 @@ lang: []const u8 = "en",
|
||||||
login_cmd: ?[]const u8 = null,
|
login_cmd: ?[]const u8 = null,
|
||||||
login_defs_path: []const u8 = "/etc/login.defs",
|
login_defs_path: []const u8 = "/etc/login.defs",
|
||||||
logout_cmd: ?[]const u8 = null,
|
logout_cmd: ?[]const u8 = null,
|
||||||
ly_log: []const u8 = "/var/log/ly.log",
|
ly_log: ?[]const u8 = "/var/log/ly.log",
|
||||||
margin_box_h: u8 = 2,
|
margin_box_h: u8 = 2,
|
||||||
margin_box_v: u8 = 1,
|
margin_box_v: u8 = 1,
|
||||||
min_refresh_delta: u16 = 5,
|
|
||||||
numlock: bool = false,
|
numlock: bool = false,
|
||||||
path: ?[]const u8 = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
path: ?[]const u8 = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||||
restart_cmd: []const u8 = "/sbin/shutdown -r now",
|
restart_cmd: []const u8 = "/sbin/shutdown -r now",
|
||||||
|
|
@ -80,6 +85,9 @@ save: bool = true,
|
||||||
service_name: [:0]const u8 = "ly",
|
service_name: [:0]const u8 = "ly",
|
||||||
session_log: ?[]const u8 = "ly-session.log",
|
session_log: ?[]const u8 = "ly-session.log",
|
||||||
setup_cmd: []const u8 = build_options.config_directory ++ "/ly/setup.sh",
|
setup_cmd: []const u8 = build_options.config_directory ++ "/ly/setup.sh",
|
||||||
|
shell: bool = true,
|
||||||
|
show_password_key: []const u8 = "F7",
|
||||||
|
show_tty: bool = false,
|
||||||
shutdown_cmd: []const u8 = "/sbin/shutdown -a now",
|
shutdown_cmd: []const u8 = "/sbin/shutdown -a now",
|
||||||
shutdown_key: []const u8 = "F1",
|
shutdown_key: []const u8 = "F1",
|
||||||
sleep_cmd: ?[]const u8 = null,
|
sleep_cmd: ?[]const u8 = null,
|
||||||
|
|
@ -88,8 +96,9 @@ start_cmd: ?[]const u8 = null,
|
||||||
text_in_center: bool = false,
|
text_in_center: bool = false,
|
||||||
vi_default_mode: ViMode = .normal,
|
vi_default_mode: ViMode = .normal,
|
||||||
vi_mode: bool = false,
|
vi_mode: bool = false,
|
||||||
waylandsessions: []const u8 = build_options.prefix_directory ++ "/share/wayland-sessions",
|
waylandsessions: ?[]const u8 = build_options.prefix_directory ++ "/share/wayland-sessions",
|
||||||
x_cmd: []const u8 = build_options.prefix_directory ++ "/bin/X",
|
x_cmd: []const u8 = build_options.prefix_directory ++ "/bin/X",
|
||||||
|
x_vt: ?u8 = null,
|
||||||
xauth_cmd: []const u8 = build_options.prefix_directory ++ "/bin/xauth",
|
xauth_cmd: []const u8 = build_options.prefix_directory ++ "/bin/xauth",
|
||||||
xinitrc: ?[]const u8 = "~/.xinitrc",
|
xinitrc: ?[]const u8 = "~/.xinitrc",
|
||||||
xsessions: []const u8 = build_options.prefix_directory ++ "/share/xsessions",
|
xsessions: ?[]const u8 = build_options.prefix_directory ++ "/share/xsessions",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ brightness_down: []const u8 = "decrease brightness",
|
||||||
brightness_up: []const u8 = "increase brightness",
|
brightness_up: []const u8 = "increase brightness",
|
||||||
capslock: []const u8 = "capslock",
|
capslock: []const u8 = "capslock",
|
||||||
custom: []const u8 = "custom",
|
custom: []const u8 = "custom",
|
||||||
|
custom_info_err_output_long: []const u8 = "output too long",
|
||||||
|
custom_info_err_no_output: []const u8 = "no output",
|
||||||
|
custom_info_err_no_output_error: []const u8 = ", possible error",
|
||||||
err_alloc: []const u8 = "failed memory allocation",
|
err_alloc: []const u8 = "failed memory allocation",
|
||||||
err_args: []const u8 = "unable to parse command line arguments",
|
err_args: []const u8 = "unable to parse command line arguments",
|
||||||
err_autologin_session: []const u8 = "autologin session not found",
|
err_autologin_session: []const u8 = "autologin session not found",
|
||||||
|
|
@ -78,6 +81,7 @@ restart: []const u8 = "reboot",
|
||||||
shell: [:0]const u8 = "shell",
|
shell: [:0]const u8 = "shell",
|
||||||
shutdown: []const u8 = "shutdown",
|
shutdown: []const u8 = "shutdown",
|
||||||
sleep: []const u8 = "sleep",
|
sleep: []const u8 = "sleep",
|
||||||
|
toggle_password: []const u8 = "toggle password",
|
||||||
wayland: []const u8 = "wayland",
|
wayland: []const u8 = "wayland",
|
||||||
x11: []const u8 = "x11",
|
x11: []const u8 = "x11",
|
||||||
xinitrc: [:0]const u8 = "xinitrc",
|
xinitrc: [:0]const u8 = "xinitrc",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const custom = @This();
|
||||||
|
|
||||||
|
pub const CustomCommandBind = struct {
|
||||||
|
name: []const u8 = "",
|
||||||
|
cmd: []const u8 = "",
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const UNDEFINED_CMD: []const u8 = "echo \"You forgot to define 'cmd'!\"";
|
||||||
|
|
||||||
|
pub const CustomCommandInfo = struct {
|
||||||
|
name: []const u8 = "",
|
||||||
|
cmd: ?[]const u8 = null,
|
||||||
|
/// To be set to the label's widget ID
|
||||||
|
id: u64 = 0,
|
||||||
|
|
||||||
|
/// In frames, the refresh rate for the `cmd` to run again
|
||||||
|
/// If 0, only run once.
|
||||||
|
refresh: u32 = 0,
|
||||||
|
counter: u32 = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub var binds: std.array_hash_map.String(CustomCommandBind) = undefined;
|
||||||
|
pub var labels: std.array_hash_map.String(CustomCommandInfo) = undefined;
|
||||||
|
|
@ -3,14 +3,20 @@
|
||||||
// Color codes interpreted differently since 1.1.0
|
// Color codes interpreted differently since 1.1.0
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const ini = @import("zigini");
|
var temporary_allocator = std.heap.page_allocator;
|
||||||
|
|
||||||
|
const ly_ui = @import("ly-ui");
|
||||||
|
const TerminalBuffer = ly_ui.TerminalBuffer;
|
||||||
|
const Color = TerminalBuffer.Color;
|
||||||
|
const Styling = TerminalBuffer.Styling;
|
||||||
|
const ly_core = ly_ui.ly_core;
|
||||||
|
const IniParser = ly_core.IniParser;
|
||||||
|
const ini = ly_core.ini;
|
||||||
|
|
||||||
const Config = @import("Config.zig");
|
const Config = @import("Config.zig");
|
||||||
const OldSave = @import("OldSave.zig");
|
const OldSave = @import("OldSave.zig");
|
||||||
const SavedUsers = @import("SavedUsers.zig");
|
const SavedUsers = @import("SavedUsers.zig");
|
||||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
const custom = @import("custom.zig");
|
||||||
|
|
||||||
const Color = TerminalBuffer.Color;
|
|
||||||
const Styling = TerminalBuffer.Styling;
|
|
||||||
|
|
||||||
const color_properties = [_][]const u8{
|
const color_properties = [_][]const u8{
|
||||||
"bg",
|
"bg",
|
||||||
|
|
@ -41,8 +47,6 @@ const removed_properties = [_][]const u8{
|
||||||
"load",
|
"load",
|
||||||
};
|
};
|
||||||
|
|
||||||
var temporary_allocator = std.heap.page_allocator;
|
|
||||||
|
|
||||||
pub var auto_eight_colors: bool = true;
|
pub var auto_eight_colors: bool = true;
|
||||||
|
|
||||||
pub var maybe_animate: ?bool = null;
|
pub var maybe_animate: ?bool = null;
|
||||||
|
|
@ -151,6 +155,69 @@ pub fn configFieldHandler(_: std.mem.Allocator, field: ini.IniField) ?ini.IniFie
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, field.key, "min_refresh_delta")) {
|
||||||
|
// The option has simply been renamed
|
||||||
|
var mapped_field = field;
|
||||||
|
mapped_field.key = "animation_frame_delay";
|
||||||
|
|
||||||
|
return mapped_field;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Dearest Melpert,
|
||||||
|
// I pray this message finds you well, as daylight dwindles and the witching hour
|
||||||
|
// approaches, I find it more and more imperative as time continues that I place
|
||||||
|
// this reminder here in such a format that you cannot ignore.
|
||||||
|
// Do you know how long I have been waiting for this petition to be authorized
|
||||||
|
// in regards to this particular segment of computerized instructions?
|
||||||
|
// It has been many a moon since this particular audit has been
|
||||||
|
// posted regarding the position of handling configurable literature
|
||||||
|
// apparatuses and plans for a new feature to the configuration
|
||||||
|
// interface and as time continues onwards I grow more restless
|
||||||
|
// on the progress of said interface, only to find out afterwards
|
||||||
|
// that you have PROCRASTINATED on the efforts meant to enhance
|
||||||
|
// configuration. Thus the requirement for this reminder larger
|
||||||
|
// compared to the two reminders regarding better methods of
|
||||||
|
// X termination detection and new usernames with existing
|
||||||
|
// save files.
|
||||||
|
//
|
||||||
|
// Thus is my que to leave this TODO at thy request,
|
||||||
|
//
|
||||||
|
// Forever Sullied,
|
||||||
|
//
|
||||||
|
// Ly Contributor.
|
||||||
|
//
|
||||||
|
if (std.mem.startsWith(u8, field.header, "cmd:")) {
|
||||||
|
const key = field.header["cmd:".len..];
|
||||||
|
const keyZ = temporary_allocator.dupe(u8, key) catch "";
|
||||||
|
if (!custom.binds.contains(key)) {
|
||||||
|
custom.binds.put(temporary_allocator, keyZ, .{}) catch {};
|
||||||
|
}
|
||||||
|
if (custom.binds.getPtr(keyZ)) |command| {
|
||||||
|
if (std.mem.eql(u8, field.key, "name")) {
|
||||||
|
command.name = temporary_allocator.dupe(u8, field.value) catch "";
|
||||||
|
}
|
||||||
|
if (std.mem.eql(u8, field.key, "cmd")) {
|
||||||
|
command.cmd = temporary_allocator.dupe(u8, field.value) catch "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std.mem.startsWith(u8, field.header, "lbl:")) {
|
||||||
|
const key = field.header["lbl:".len..];
|
||||||
|
const keyZ = temporary_allocator.dupe(u8, key) catch "";
|
||||||
|
if (!custom.labels.contains(keyZ)) {
|
||||||
|
custom.labels.put(temporary_allocator, keyZ, .{ .name = keyZ }) catch {};
|
||||||
|
}
|
||||||
|
if (custom.labels.getPtr(keyZ)) |label| {
|
||||||
|
if (std.mem.eql(u8, field.key, "cmd")) {
|
||||||
|
label.cmd = temporary_allocator.dupe(u8, field.value) catch "";
|
||||||
|
}
|
||||||
|
if (std.mem.eql(u8, field.key, "refresh")) {
|
||||||
|
label.refresh = std.fmt.parseInt(u32, field.value, 10) catch 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,51 +254,14 @@ pub fn lateConfigFieldHandler(config: *Config) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tryMigrateFirstSaveFile(user_buf: *[32]u8) OldSave {
|
pub fn tryMigrateIniSaveFile(allocator: std.mem.Allocator, io: std.Io, path: []const u8, saved_users: *SavedUsers, usernames: [][]const u8) !?IniParser(OldSave) {
|
||||||
var save = OldSave{};
|
var save_parser = try IniParser(OldSave).init(allocator, io, path, null);
|
||||||
|
errdefer save_parser.deinit();
|
||||||
if (maybe_save_file) |path| {
|
|
||||||
defer temporary_allocator.free(path);
|
|
||||||
|
|
||||||
var file = std.fs.openFileAbsolute(path, .{}) catch return save;
|
|
||||||
defer file.close();
|
|
||||||
|
|
||||||
var file_buffer: [64]u8 = undefined;
|
|
||||||
var file_reader = file.reader(&file_buffer);
|
|
||||||
var reader = &file_reader.interface;
|
|
||||||
|
|
||||||
var user_writer = std.Io.Writer.fixed(user_buf);
|
|
||||||
var written = reader.streamDelimiter(&user_writer, '\n') catch return save;
|
|
||||||
if (written > 0) save.user = user_buf[0..written];
|
|
||||||
|
|
||||||
var session_buf: [20]u8 = undefined;
|
|
||||||
var session_writer = std.Io.Writer.fixed(&session_buf);
|
|
||||||
written = reader.streamDelimiter(&session_writer, '\n') catch return save;
|
|
||||||
|
|
||||||
var session_index: ?usize = null;
|
|
||||||
if (written > 0) {
|
|
||||||
session_index = std.fmt.parseUnsigned(usize, session_buf[0..written], 10) catch return save;
|
|
||||||
}
|
|
||||||
save.session_index = session_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
return save;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tryMigrateIniSaveFile(allocator: std.mem.Allocator, save_ini: *ini.Ini(OldSave), path: []const u8, saved_users: *SavedUsers, usernames: [][]const u8) !bool {
|
|
||||||
var old_save_file_exists = true;
|
|
||||||
|
|
||||||
var user_buf: [32]u8 = undefined;
|
var user_buf: [32]u8 = undefined;
|
||||||
const save = save_ini.readFileToStruct(path, .{
|
const maybe_save = if (save_parser.maybe_load_error == null) save_parser.structure else tryMigrateFirstSaveFile(io, &user_buf);
|
||||||
.fieldHandler = null,
|
|
||||||
.comment_characters = "#",
|
|
||||||
}) catch no_save_file: {
|
|
||||||
old_save_file_exists = false;
|
|
||||||
break :no_save_file tryMigrateFirstSaveFile(&user_buf);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!old_save_file_exists) return false;
|
|
||||||
|
|
||||||
|
if (maybe_save) |save| {
|
||||||
// Add all other users to the list
|
// Add all other users to the list
|
||||||
for (usernames, 0..) |username, i| {
|
for (usernames, 0..) |username, i| {
|
||||||
if (save.user) |user| {
|
if (save.user) |user| {
|
||||||
|
|
@ -246,5 +276,40 @@ pub fn tryMigrateIniSaveFile(allocator: std.mem.Allocator, save_ini: *ini.Ini(Ol
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return save_parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tryMigrateFirstSaveFile(io: std.Io, user_buf: *[32]u8) ?OldSave {
|
||||||
|
if (maybe_save_file) |path| {
|
||||||
|
defer temporary_allocator.free(path);
|
||||||
|
|
||||||
|
var save = OldSave{};
|
||||||
|
var file = std.Io.Dir.openFileAbsolute(io, path, .{}) catch return null;
|
||||||
|
defer file.close(io);
|
||||||
|
|
||||||
|
var file_buffer: [64]u8 = undefined;
|
||||||
|
var file_reader = file.reader(io, &file_buffer);
|
||||||
|
var reader = &file_reader.interface;
|
||||||
|
|
||||||
|
var user_writer = std.Io.Writer.fixed(user_buf);
|
||||||
|
var written = reader.streamDelimiter(&user_writer, '\n') catch return null;
|
||||||
|
if (written > 0) save.user = user_buf[0..written];
|
||||||
|
|
||||||
|
var session_buf: [20]u8 = undefined;
|
||||||
|
var session_writer = std.Io.Writer.fixed(&session_buf);
|
||||||
|
written = reader.streamDelimiter(&session_writer, '\n') catch return null;
|
||||||
|
|
||||||
|
var session_index: ?usize = null;
|
||||||
|
if (written > 0) {
|
||||||
|
session_index = std.fmt.parseUnsigned(usize, session_buf[0..written], 10) catch return null;
|
||||||
|
}
|
||||||
|
save.session_index = session_index;
|
||||||
|
|
||||||
|
return save;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub const Animation = enum {
|
pub const Animation = enum {
|
||||||
none,
|
none,
|
||||||
doom,
|
doom,
|
||||||
|
|
@ -53,3 +54,15 @@ pub const Bigclock = enum {
|
||||||
en,
|
en,
|
||||||
fa,
|
fa,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const DurOffsetAlignment = enum {
|
||||||
|
topleft,
|
||||||
|
topcenter,
|
||||||
|
topright,
|
||||||
|
centerleft,
|
||||||
|
center,
|
||||||
|
centerright,
|
||||||
|
bottomleft,
|
||||||
|
bottomcenter,
|
||||||
|
bottomright,
|
||||||
|
};
|
||||||
|
|
|
||||||
2712
src/main.zig
2712
src/main.zig
File diff suppressed because it is too large
Load Diff
|
|
@ -1,61 +0,0 @@
|
||||||
const Animation = @This();
|
|
||||||
|
|
||||||
const VTable = struct {
|
|
||||||
deinit_fn: *const fn (ptr: *anyopaque) void,
|
|
||||||
realloc_fn: *const fn (ptr: *anyopaque) anyerror!void,
|
|
||||||
draw_fn: *const fn (ptr: *anyopaque) void,
|
|
||||||
};
|
|
||||||
|
|
||||||
pointer: *anyopaque,
|
|
||||||
vtable: VTable,
|
|
||||||
|
|
||||||
pub fn init(
|
|
||||||
pointer: anytype,
|
|
||||||
comptime deinit_fn: fn (ptr: @TypeOf(pointer)) void,
|
|
||||||
comptime realloc_fn: fn (ptr: @TypeOf(pointer)) anyerror!void,
|
|
||||||
comptime draw_fn: fn (ptr: @TypeOf(pointer)) void,
|
|
||||||
) Animation {
|
|
||||||
const Pointer = @TypeOf(pointer);
|
|
||||||
const Impl = struct {
|
|
||||||
pub fn deinitImpl(ptr: *anyopaque) void {
|
|
||||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
|
||||||
return @call(.always_inline, deinit_fn, .{impl});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reallocImpl(ptr: *anyopaque) anyerror!void {
|
|
||||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
|
||||||
return @call(.always_inline, realloc_fn, .{impl});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drawImpl(ptr: *anyopaque) void {
|
|
||||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
|
||||||
return @call(.always_inline, draw_fn, .{impl});
|
|
||||||
}
|
|
||||||
|
|
||||||
const vtable = VTable{
|
|
||||||
.deinit_fn = deinitImpl,
|
|
||||||
.realloc_fn = reallocImpl,
|
|
||||||
.draw_fn = drawImpl,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.pointer = pointer,
|
|
||||||
.vtable = Impl.vtable,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *Animation) void {
|
|
||||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
|
||||||
return @call(.auto, self.vtable.deinit_fn, .{impl});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn realloc(self: *Animation) anyerror!void {
|
|
||||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
|
||||||
return @call(.auto, self.vtable.realloc_fn, .{impl});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw(self: *Animation) void {
|
|
||||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
|
||||||
return @call(.auto, self.vtable.draw_fn, .{impl});
|
|
||||||
}
|
|
||||||
|
|
@ -1,260 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const interop = @import("../interop.zig");
|
|
||||||
const Cell = @import("Cell.zig");
|
|
||||||
|
|
||||||
const Random = std.Random;
|
|
||||||
|
|
||||||
const termbox = interop.termbox;
|
|
||||||
|
|
||||||
const TerminalBuffer = @This();
|
|
||||||
|
|
||||||
pub const InitOptions = struct {
|
|
||||||
fg: u32,
|
|
||||||
bg: u32,
|
|
||||||
border_fg: u32,
|
|
||||||
margin_box_h: u8,
|
|
||||||
margin_box_v: u8,
|
|
||||||
input_len: u8,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Styling = struct {
|
|
||||||
pub const BOLD = termbox.TB_BOLD;
|
|
||||||
pub const UNDERLINE = termbox.TB_UNDERLINE;
|
|
||||||
pub const REVERSE = termbox.TB_REVERSE;
|
|
||||||
pub const ITALIC = termbox.TB_ITALIC;
|
|
||||||
pub const BLINK = termbox.TB_BLINK;
|
|
||||||
pub const HI_BLACK = termbox.TB_HI_BLACK;
|
|
||||||
pub const BRIGHT = termbox.TB_BRIGHT;
|
|
||||||
pub const DIM = termbox.TB_DIM;
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Color = struct {
|
|
||||||
pub const DEFAULT = 0x00000000;
|
|
||||||
pub const TRUE_BLACK = Styling.HI_BLACK;
|
|
||||||
pub const TRUE_RED = 0x00FF0000;
|
|
||||||
pub const TRUE_GREEN = 0x0000FF00;
|
|
||||||
pub const TRUE_YELLOW = 0x00FFFF00;
|
|
||||||
pub const TRUE_BLUE = 0x000000FF;
|
|
||||||
pub const TRUE_MAGENTA = 0x00FF00FF;
|
|
||||||
pub const TRUE_CYAN = 0x0000FFFF;
|
|
||||||
pub const TRUE_WHITE = 0x00FFFFFF;
|
|
||||||
pub const TRUE_DIM_RED = 0x00800000;
|
|
||||||
pub const TRUE_DIM_GREEN = 0x00008000;
|
|
||||||
pub const TRUE_DIM_YELLOW = 0x00808000;
|
|
||||||
pub const TRUE_DIM_BLUE = 0x00000080;
|
|
||||||
pub const TRUE_DIM_MAGENTA = 0x00800080;
|
|
||||||
pub const TRUE_DIM_CYAN = 0x00008080;
|
|
||||||
pub const TRUE_DIM_WHITE = 0x00C0C0C0;
|
|
||||||
pub const ECOL_BLACK = 1;
|
|
||||||
pub const ECOL_RED = 2;
|
|
||||||
pub const ECOL_GREEN = 3;
|
|
||||||
pub const ECOL_YELLOW = 4;
|
|
||||||
pub const ECOL_BLUE = 5;
|
|
||||||
pub const ECOL_MAGENTA = 6;
|
|
||||||
pub const ECOL_CYAN = 7;
|
|
||||||
pub const ECOL_WHITE = 8;
|
|
||||||
};
|
|
||||||
|
|
||||||
random: Random,
|
|
||||||
width: usize,
|
|
||||||
height: usize,
|
|
||||||
fg: u32,
|
|
||||||
bg: u32,
|
|
||||||
border_fg: u32,
|
|
||||||
box_chars: struct {
|
|
||||||
left_up: u32,
|
|
||||||
left_down: u32,
|
|
||||||
right_up: u32,
|
|
||||||
right_down: u32,
|
|
||||||
top: u32,
|
|
||||||
bottom: u32,
|
|
||||||
left: u32,
|
|
||||||
right: u32,
|
|
||||||
},
|
|
||||||
labels_max_length: usize,
|
|
||||||
box_x: usize,
|
|
||||||
box_y: usize,
|
|
||||||
box_width: usize,
|
|
||||||
box_height: usize,
|
|
||||||
margin_box_v: u8,
|
|
||||||
margin_box_h: u8,
|
|
||||||
blank_cell: Cell,
|
|
||||||
|
|
||||||
pub fn init(options: InitOptions, labels_max_length: usize, random: Random) TerminalBuffer {
|
|
||||||
return .{
|
|
||||||
.random = random,
|
|
||||||
.width = @intCast(termbox.tb_width()),
|
|
||||||
.height = @intCast(termbox.tb_height()),
|
|
||||||
.fg = options.fg,
|
|
||||||
.bg = options.bg,
|
|
||||||
.border_fg = options.border_fg,
|
|
||||||
.box_chars = if (interop.supportsUnicode()) .{
|
|
||||||
.left_up = 0x250C,
|
|
||||||
.left_down = 0x2514,
|
|
||||||
.right_up = 0x2510,
|
|
||||||
.right_down = 0x2518,
|
|
||||||
.top = 0x2500,
|
|
||||||
.bottom = 0x2500,
|
|
||||||
.left = 0x2502,
|
|
||||||
.right = 0x2502,
|
|
||||||
} else .{
|
|
||||||
.left_up = '+',
|
|
||||||
.left_down = '+',
|
|
||||||
.right_up = '+',
|
|
||||||
.right_down = '+',
|
|
||||||
.top = '-',
|
|
||||||
.bottom = '-',
|
|
||||||
.left = '|',
|
|
||||||
.right = '|',
|
|
||||||
},
|
|
||||||
.labels_max_length = labels_max_length,
|
|
||||||
.box_x = 0,
|
|
||||||
.box_y = 0,
|
|
||||||
.box_width = (2 * options.margin_box_h) + options.input_len + 1 + labels_max_length,
|
|
||||||
.box_height = 7 + (2 * options.margin_box_v),
|
|
||||||
.margin_box_v = options.margin_box_v,
|
|
||||||
.margin_box_h = options.margin_box_h,
|
|
||||||
.blank_cell = Cell.init(' ', options.fg, options.bg),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cascade(self: TerminalBuffer) bool {
|
|
||||||
var changed = false;
|
|
||||||
var y = self.height - 2;
|
|
||||||
|
|
||||||
while (y > 0) : (y -= 1) {
|
|
||||||
for (0..self.width) |x| {
|
|
||||||
var cell: ?*termbox.tb_cell = undefined;
|
|
||||||
var cell_under: ?*termbox.tb_cell = undefined;
|
|
||||||
|
|
||||||
_ = termbox.tb_get_cell(@intCast(x), @intCast(y - 1), 1, &cell);
|
|
||||||
_ = termbox.tb_get_cell(@intCast(x), @intCast(y), 1, &cell_under);
|
|
||||||
|
|
||||||
// This shouldn't happen under normal circumstances, but because
|
|
||||||
// this is a *secret* animation, there's no need to care that much
|
|
||||||
if (cell == null or cell_under == null) continue;
|
|
||||||
|
|
||||||
const char: u8 = @truncate(cell.?.ch);
|
|
||||||
if (std.ascii.isWhitespace(char)) continue;
|
|
||||||
|
|
||||||
const char_under: u8 = @truncate(cell_under.?.ch);
|
|
||||||
if (!std.ascii.isWhitespace(char_under)) continue;
|
|
||||||
|
|
||||||
changed = true;
|
|
||||||
|
|
||||||
if ((self.random.int(u16) % 10) > 7) continue;
|
|
||||||
|
|
||||||
_ = termbox.tb_set_cell(@intCast(x), @intCast(y), cell.?.ch, cell.?.fg, cell.?.bg);
|
|
||||||
_ = termbox.tb_set_cell(@intCast(x), @intCast(y - 1), ' ', cell_under.?.fg, cell_under.?.bg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drawBoxCenter(self: *TerminalBuffer, show_borders: bool, blank_box: bool) void {
|
|
||||||
if (self.width < 2 or self.height < 2) return;
|
|
||||||
const x1 = (self.width - @min(self.width - 2, self.box_width)) / 2;
|
|
||||||
const y1 = (self.height - @min(self.height - 2, self.box_height)) / 2;
|
|
||||||
const x2 = (self.width + @min(self.width, self.box_width)) / 2;
|
|
||||||
const y2 = (self.height + @min(self.height, self.box_height)) / 2;
|
|
||||||
|
|
||||||
self.box_x = x1;
|
|
||||||
self.box_y = y1;
|
|
||||||
|
|
||||||
if (show_borders) {
|
|
||||||
_ = termbox.tb_set_cell(@intCast(x1 - 1), @intCast(y1 - 1), self.box_chars.left_up, self.border_fg, self.bg);
|
|
||||||
_ = termbox.tb_set_cell(@intCast(x2), @intCast(y1 - 1), self.box_chars.right_up, self.border_fg, self.bg);
|
|
||||||
_ = termbox.tb_set_cell(@intCast(x1 - 1), @intCast(y2), self.box_chars.left_down, self.border_fg, self.bg);
|
|
||||||
_ = termbox.tb_set_cell(@intCast(x2), @intCast(y2), self.box_chars.right_down, self.border_fg, self.bg);
|
|
||||||
|
|
||||||
var c1 = Cell.init(self.box_chars.top, self.border_fg, self.bg);
|
|
||||||
var c2 = Cell.init(self.box_chars.bottom, self.border_fg, self.bg);
|
|
||||||
|
|
||||||
for (0..self.box_width) |i| {
|
|
||||||
c1.put(x1 + i, y1 - 1);
|
|
||||||
c2.put(x1 + i, y2);
|
|
||||||
}
|
|
||||||
|
|
||||||
c1.ch = self.box_chars.left;
|
|
||||||
c2.ch = self.box_chars.right;
|
|
||||||
|
|
||||||
for (0..self.box_height) |i| {
|
|
||||||
c1.put(x1 - 1, y1 + i);
|
|
||||||
c2.put(x2, y1 + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blank_box) {
|
|
||||||
for (0..self.box_height) |y| {
|
|
||||||
for (0..self.box_width) |x| {
|
|
||||||
self.blank_cell.put(x1 + x, y1 + y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn calculateComponentCoordinates(self: TerminalBuffer) struct {
|
|
||||||
start_x: usize,
|
|
||||||
x: usize,
|
|
||||||
y: usize,
|
|
||||||
full_visible_length: usize,
|
|
||||||
visible_length: usize,
|
|
||||||
} {
|
|
||||||
const start_x = self.box_x + self.margin_box_h;
|
|
||||||
const x = start_x + self.labels_max_length + 1;
|
|
||||||
const y = self.box_y + self.margin_box_v;
|
|
||||||
const full_visible_length = self.box_x + self.box_width - self.margin_box_h - start_x;
|
|
||||||
const visible_length = self.box_x + self.box_width - self.margin_box_h - x;
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.start_x = start_x,
|
|
||||||
.x = x,
|
|
||||||
.y = y,
|
|
||||||
.full_visible_length = full_visible_length,
|
|
||||||
.visible_length = visible_length,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drawLabel(self: TerminalBuffer, text: []const u8, x: usize, y: usize) void {
|
|
||||||
drawColorLabel(text, x, y, self.fg, self.bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drawColorLabel(text: []const u8, x: usize, y: usize, fg: u32, bg: u32) void {
|
|
||||||
const yc: c_int = @intCast(y);
|
|
||||||
const utf8view = std.unicode.Utf8View.init(text) catch return;
|
|
||||||
var utf8 = utf8view.iterator();
|
|
||||||
|
|
||||||
var i: c_int = @intCast(x);
|
|
||||||
while (utf8.nextCodepoint()) |codepoint| : (i += termbox.tb_wcwidth(codepoint)) {
|
|
||||||
_ = termbox.tb_set_cell(i, yc, codepoint, fg, bg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drawConfinedLabel(self: TerminalBuffer, text: []const u8, x: usize, y: usize, max_length: usize) void {
|
|
||||||
const yc: c_int = @intCast(y);
|
|
||||||
const utf8view = std.unicode.Utf8View.init(text) catch return;
|
|
||||||
var utf8 = utf8view.iterator();
|
|
||||||
|
|
||||||
var i: c_int = @intCast(x);
|
|
||||||
while (utf8.nextCodepoint()) |codepoint| : (i += termbox.tb_wcwidth(codepoint)) {
|
|
||||||
if (i - @as(c_int, @intCast(x)) >= max_length) break;
|
|
||||||
_ = termbox.tb_set_cell(i, yc, codepoint, self.fg, self.bg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drawCharMultiple(self: TerminalBuffer, char: u32, x: usize, y: usize, length: usize) void {
|
|
||||||
const cell = Cell.init(char, self.fg, self.bg);
|
|
||||||
for (0..length) |xx| cell.put(x + xx, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Every codepoint is assumed to have a width of 1.
|
|
||||||
// Since Ly is normally running in a TTY, this should be fine.
|
|
||||||
pub fn strWidth(str: []const u8) !u8 {
|
|
||||||
const utf8view = try std.unicode.Utf8View.init(str);
|
|
||||||
var utf8 = utf8view.iterator();
|
|
||||||
var i: c_int = 0;
|
|
||||||
while (utf8.nextCodepoint()) |codepoint| i += termbox.tb_wcwidth(codepoint);
|
|
||||||
|
|
||||||
return @intCast(i);
|
|
||||||
}
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
|
||||||
const generic = @import("generic.zig");
|
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
|
|
||||||
const MessageLabel = generic.CyclableLabel(Message, Message);
|
|
||||||
|
|
||||||
const InfoLine = @This();
|
|
||||||
|
|
||||||
const Message = struct {
|
|
||||||
width: u8,
|
|
||||||
text: []const u8,
|
|
||||||
bg: u32,
|
|
||||||
fg: u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
label: MessageLabel,
|
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer) InfoLine {
|
|
||||||
return .{
|
|
||||||
.label = MessageLabel.init(allocator, buffer, drawItem, null, null),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *InfoLine) void {
|
|
||||||
self.label.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn addMessage(self: *InfoLine, text: []const u8, bg: u32, fg: u32) !void {
|
|
||||||
if (text.len == 0) return;
|
|
||||||
|
|
||||||
try self.label.addItem(.{
|
|
||||||
.width = try TerminalBuffer.strWidth(text),
|
|
||||||
.text = text,
|
|
||||||
.bg = bg,
|
|
||||||
.fg = fg,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clearRendered(allocator: Allocator, buffer: TerminalBuffer) !void {
|
|
||||||
// Draw over the area
|
|
||||||
const y = buffer.box_y + buffer.margin_box_v;
|
|
||||||
const spaces = try allocator.alloc(u8, buffer.box_width);
|
|
||||||
defer allocator.free(spaces);
|
|
||||||
|
|
||||||
@memset(spaces, ' ');
|
|
||||||
|
|
||||||
buffer.drawLabel(spaces, buffer.box_x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drawItem(label: *MessageLabel, message: Message, _: usize, _: usize) bool {
|
|
||||||
if (message.width == 0 or label.buffer.box_width <= message.width) return false;
|
|
||||||
|
|
||||||
const x = label.buffer.box_x + ((label.buffer.box_width - message.width) / 2);
|
|
||||||
label.first_char_x = x + message.width;
|
|
||||||
|
|
||||||
TerminalBuffer.drawColorLabel(message.text, x, label.y, message.fg, message.bg);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
|
||||||
const enums = @import("../../enums.zig");
|
|
||||||
const Environment = @import("../../Environment.zig");
|
|
||||||
const generic = @import("generic.zig");
|
|
||||||
const UserList = @import("UserList.zig");
|
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const DisplayServer = enums.DisplayServer;
|
|
||||||
|
|
||||||
const Env = struct {
|
|
||||||
environment: Environment,
|
|
||||||
index: usize,
|
|
||||||
};
|
|
||||||
const EnvironmentLabel = generic.CyclableLabel(Env, *UserList);
|
|
||||||
|
|
||||||
const Session = @This();
|
|
||||||
|
|
||||||
label: EnvironmentLabel,
|
|
||||||
user_list: *UserList,
|
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, user_list: *UserList) Session {
|
|
||||||
return .{
|
|
||||||
.label = EnvironmentLabel.init(allocator, buffer, drawItem, sessionChanged, user_list),
|
|
||||||
.user_list = user_list,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *Session) void {
|
|
||||||
for (self.label.list.items) |*env| {
|
|
||||||
if (env.environment.entry_ini) |*entry_ini| entry_ini.deinit();
|
|
||||||
if (env.environment.xdg_session_desktop_owned) {
|
|
||||||
self.label.allocator.free(env.environment.xdg_session_desktop.?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.label.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn addEnvironment(self: *Session, environment: Environment) !void {
|
|
||||||
const env = Env{ .environment = environment, .index = self.label.list.items.len };
|
|
||||||
|
|
||||||
try self.label.addItem(env);
|
|
||||||
sessionChanged(env, self.user_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sessionChanged(env: Env, maybe_user_list: ?*UserList) void {
|
|
||||||
if (maybe_user_list) |user_list| {
|
|
||||||
const user = user_list.label.list.items[user_list.label.current];
|
|
||||||
if (!user.first_run) return;
|
|
||||||
|
|
||||||
user.session_index.* = env.index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drawItem(label: *EnvironmentLabel, env: Env, x: usize, y: usize) bool {
|
|
||||||
const length = @min(env.environment.name.len, label.visible_length - 3);
|
|
||||||
if (length == 0) return false;
|
|
||||||
|
|
||||||
const nx = if (label.text_in_center) (label.x + (label.visible_length - env.environment.name.len) / 2) else (label.x + 2);
|
|
||||||
label.first_char_x = nx + env.environment.name.len;
|
|
||||||
|
|
||||||
label.buffer.drawLabel(env.environment.specifier, x, y);
|
|
||||||
label.buffer.drawLabel(env.environment.name, nx, label.y);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const interop = @import("../../interop.zig");
|
|
||||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const DynamicString = std.ArrayListUnmanaged(u8);
|
|
||||||
|
|
||||||
const termbox = interop.termbox;
|
|
||||||
|
|
||||||
const Text = @This();
|
|
||||||
|
|
||||||
allocator: Allocator,
|
|
||||||
buffer: *TerminalBuffer,
|
|
||||||
text: DynamicString,
|
|
||||||
end: usize,
|
|
||||||
cursor: usize,
|
|
||||||
visible_start: usize,
|
|
||||||
visible_length: usize,
|
|
||||||
x: usize,
|
|
||||||
y: usize,
|
|
||||||
masked: bool,
|
|
||||||
maybe_mask: ?u32,
|
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, masked: bool, maybe_mask: ?u32) Text {
|
|
||||||
const text: DynamicString = .empty;
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.allocator = allocator,
|
|
||||||
.buffer = buffer,
|
|
||||||
.text = text,
|
|
||||||
.end = 0,
|
|
||||||
.cursor = 0,
|
|
||||||
.visible_start = 0,
|
|
||||||
.visible_length = 0,
|
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
|
||||||
.masked = masked,
|
|
||||||
.maybe_mask = maybe_mask,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *Text) void {
|
|
||||||
self.text.deinit(self.allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn position(self: *Text, x: usize, y: usize, visible_length: usize) void {
|
|
||||||
self.x = x;
|
|
||||||
self.y = y;
|
|
||||||
self.visible_length = visible_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle(self: *Text, maybe_event: ?*termbox.tb_event, insert_mode: bool) !void {
|
|
||||||
if (maybe_event) |event| blk: {
|
|
||||||
if (event.type != termbox.TB_EVENT_KEY) break :blk;
|
|
||||||
|
|
||||||
switch (event.key) {
|
|
||||||
termbox.TB_KEY_ARROW_LEFT => self.goLeft(),
|
|
||||||
termbox.TB_KEY_ARROW_RIGHT => self.goRight(),
|
|
||||||
termbox.TB_KEY_DELETE => self.delete(),
|
|
||||||
termbox.TB_KEY_BACKSPACE, termbox.TB_KEY_BACKSPACE2 => {
|
|
||||||
if (insert_mode) {
|
|
||||||
self.backspace();
|
|
||||||
} else {
|
|
||||||
self.goLeft();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
termbox.TB_KEY_SPACE => try self.write(' '),
|
|
||||||
else => {
|
|
||||||
if (event.ch > 31 and event.ch < 127) {
|
|
||||||
if (insert_mode) {
|
|
||||||
try self.write(@intCast(event.ch));
|
|
||||||
} else {
|
|
||||||
switch (event.ch) {
|
|
||||||
'h' => self.goLeft(),
|
|
||||||
'l' => self.goRight(),
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.masked and self.maybe_mask == null) {
|
|
||||||
_ = termbox.tb_set_cursor(@intCast(self.x), @intCast(self.y));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = termbox.tb_set_cursor(@intCast(self.x + (self.cursor - self.visible_start)), @intCast(self.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw(self: Text) void {
|
|
||||||
if (self.masked) {
|
|
||||||
if (self.maybe_mask) |mask| {
|
|
||||||
const length = @min(self.text.items.len, self.visible_length - 1);
|
|
||||||
if (length == 0) return;
|
|
||||||
|
|
||||||
self.buffer.drawCharMultiple(mask, self.x, self.y, length);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const length = @min(self.text.items.len, self.visible_length);
|
|
||||||
if (length == 0) return;
|
|
||||||
|
|
||||||
const visible_slice = vs: {
|
|
||||||
if (self.text.items.len > self.visible_length and self.cursor < self.text.items.len) {
|
|
||||||
break :vs self.text.items[self.visible_start..(self.visible_length + self.visible_start)];
|
|
||||||
} else {
|
|
||||||
break :vs self.text.items[self.visible_start..];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.buffer.drawLabel(visible_slice, self.x, self.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(self: *Text) void {
|
|
||||||
self.text.clearRetainingCapacity();
|
|
||||||
self.end = 0;
|
|
||||||
self.cursor = 0;
|
|
||||||
self.visible_start = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goLeft(self: *Text) void {
|
|
||||||
if (self.cursor == 0) return;
|
|
||||||
if (self.visible_start > 0) self.visible_start -= 1;
|
|
||||||
|
|
||||||
self.cursor -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goRight(self: *Text) void {
|
|
||||||
if (self.cursor >= self.end) return;
|
|
||||||
if (self.cursor - self.visible_start == self.visible_length - 1) self.visible_start += 1;
|
|
||||||
|
|
||||||
self.cursor += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn delete(self: *Text) void {
|
|
||||||
if (self.cursor >= self.end) return;
|
|
||||||
|
|
||||||
_ = self.text.orderedRemove(self.cursor);
|
|
||||||
|
|
||||||
self.end -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn backspace(self: *Text) void {
|
|
||||||
if (self.cursor == 0) return;
|
|
||||||
|
|
||||||
self.goLeft();
|
|
||||||
self.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(self: *Text, char: u8) !void {
|
|
||||||
if (char == 0) return;
|
|
||||||
|
|
||||||
try self.text.insert(self.allocator, self.cursor, char);
|
|
||||||
|
|
||||||
self.end += 1;
|
|
||||||
self.goRight();
|
|
||||||
}
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
|
||||||
const generic = @import("generic.zig");
|
|
||||||
const Session = @import("Session.zig");
|
|
||||||
const SavedUsers = @import("../../config/SavedUsers.zig");
|
|
||||||
|
|
||||||
const StringList = std.ArrayListUnmanaged([]const u8);
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
|
|
||||||
pub const User = struct {
|
|
||||||
name: []const u8,
|
|
||||||
session_index: *usize,
|
|
||||||
allocated_index: bool,
|
|
||||||
first_run: bool,
|
|
||||||
};
|
|
||||||
const UserLabel = generic.CyclableLabel(User, *Session);
|
|
||||||
|
|
||||||
const UserList = @This();
|
|
||||||
|
|
||||||
label: UserLabel,
|
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, usernames: StringList, saved_users: *SavedUsers, session: *Session) !UserList {
|
|
||||||
var userList = UserList{
|
|
||||||
.label = UserLabel.init(allocator, buffer, drawItem, usernameChanged, session),
|
|
||||||
};
|
|
||||||
|
|
||||||
for (usernames.items) |username| {
|
|
||||||
if (username.len == 0) continue;
|
|
||||||
|
|
||||||
var maybe_session_index: ?*usize = null;
|
|
||||||
var first_run = true;
|
|
||||||
for (saved_users.user_list.items) |*saved_user| {
|
|
||||||
if (std.mem.eql(u8, username, saved_user.username)) {
|
|
||||||
maybe_session_index = &saved_user.session_index;
|
|
||||||
first_run = saved_user.first_run;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var allocated_index = false;
|
|
||||||
if (maybe_session_index == null) {
|
|
||||||
maybe_session_index = try allocator.create(usize);
|
|
||||||
maybe_session_index.?.* = 0;
|
|
||||||
allocated_index = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try userList.label.addItem(.{
|
|
||||||
.name = username,
|
|
||||||
.session_index = maybe_session_index.?,
|
|
||||||
.allocated_index = allocated_index,
|
|
||||||
.first_run = first_run,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return userList;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *UserList) void {
|
|
||||||
for (self.label.list.items) |user| {
|
|
||||||
if (user.allocated_index) {
|
|
||||||
self.label.allocator.destroy(user.session_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.label.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getCurrentUsername(self: UserList) []const u8 {
|
|
||||||
return self.label.list.items[self.label.current].name;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usernameChanged(user: User, maybe_session: ?*Session) void {
|
|
||||||
if (maybe_session) |session| {
|
|
||||||
if (user.session_index.* >= session.label.list.items.len) return;
|
|
||||||
session.label.current = user.session_index.*;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drawItem(label: *UserLabel, user: User, _: usize, _: usize) bool {
|
|
||||||
const length = @min(user.name.len, label.visible_length - 3);
|
|
||||||
if (length == 0) return false;
|
|
||||||
|
|
||||||
const x = if (label.text_in_center) (label.x + (label.visible_length - user.name.len) / 2) else (label.x + 2);
|
|
||||||
label.first_char_x = x + user.name.len;
|
|
||||||
|
|
||||||
label.buffer.drawLabel(user.name, x, label.y);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const interop = @import("../../interop.zig");
|
|
||||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
|
||||||
|
|
||||||
pub fn CyclableLabel(comptime ItemType: type, comptime ChangeItemType: type) type {
|
|
||||||
return struct {
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const ItemList = std.ArrayListUnmanaged(ItemType);
|
|
||||||
const DrawItemFn = *const fn (*Self, ItemType, usize, usize) bool;
|
|
||||||
const ChangeItemFn = *const fn (ItemType, ?ChangeItemType) void;
|
|
||||||
|
|
||||||
const termbox = interop.termbox;
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
allocator: Allocator,
|
|
||||||
buffer: *TerminalBuffer,
|
|
||||||
list: ItemList,
|
|
||||||
current: usize,
|
|
||||||
visible_length: usize,
|
|
||||||
x: usize,
|
|
||||||
y: usize,
|
|
||||||
first_char_x: usize,
|
|
||||||
text_in_center: bool,
|
|
||||||
draw_item_fn: DrawItemFn,
|
|
||||||
change_item_fn: ?ChangeItemFn,
|
|
||||||
change_item_arg: ?ChangeItemType,
|
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, draw_item_fn: DrawItemFn, change_item_fn: ?ChangeItemFn, change_item_arg: ?ChangeItemType) Self {
|
|
||||||
return .{
|
|
||||||
.allocator = allocator,
|
|
||||||
.buffer = buffer,
|
|
||||||
.list = .empty,
|
|
||||||
.current = 0,
|
|
||||||
.visible_length = 0,
|
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
|
||||||
.first_char_x = 0,
|
|
||||||
.text_in_center = false,
|
|
||||||
.draw_item_fn = draw_item_fn,
|
|
||||||
.change_item_fn = change_item_fn,
|
|
||||||
.change_item_arg = change_item_arg,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
|
||||||
self.list.deinit(self.allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn position(self: *Self, x: usize, y: usize, visible_length: usize, text_in_center: ?bool) void {
|
|
||||||
self.x = x;
|
|
||||||
self.y = y;
|
|
||||||
self.visible_length = visible_length;
|
|
||||||
self.first_char_x = x + 2;
|
|
||||||
if (text_in_center) |value| {
|
|
||||||
self.text_in_center = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn addItem(self: *Self, item: ItemType) !void {
|
|
||||||
try self.list.append(self.allocator, item);
|
|
||||||
self.current = self.list.items.len - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle(self: *Self, maybe_event: ?*termbox.tb_event, insert_mode: bool) void {
|
|
||||||
if (maybe_event) |event| blk: {
|
|
||||||
if (event.type != termbox.TB_EVENT_KEY) break :blk;
|
|
||||||
|
|
||||||
switch (event.key) {
|
|
||||||
termbox.TB_KEY_ARROW_LEFT, termbox.TB_KEY_CTRL_H => self.goLeft(),
|
|
||||||
termbox.TB_KEY_ARROW_RIGHT, termbox.TB_KEY_CTRL_L => self.goRight(),
|
|
||||||
else => {
|
|
||||||
if (!insert_mode) {
|
|
||||||
switch (event.ch) {
|
|
||||||
'h' => self.goLeft(),
|
|
||||||
'l' => self.goRight(),
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = termbox.tb_set_cursor(@intCast(self.first_char_x), @intCast(self.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw(self: *Self) void {
|
|
||||||
if (self.list.items.len == 0) return;
|
|
||||||
|
|
||||||
const current_item = self.list.items[self.current];
|
|
||||||
const x = self.buffer.box_x + self.buffer.margin_box_h;
|
|
||||||
const y = self.buffer.box_y + self.buffer.margin_box_v + 2;
|
|
||||||
|
|
||||||
const continue_drawing = @call(.auto, self.draw_item_fn, .{ self, current_item, x, y });
|
|
||||||
if (!continue_drawing) return;
|
|
||||||
|
|
||||||
_ = termbox.tb_set_cell(@intCast(self.x), @intCast(self.y), '<', self.buffer.fg, self.buffer.bg);
|
|
||||||
_ = termbox.tb_set_cell(@intCast(self.x + self.visible_length - 1), @intCast(self.y), '>', self.buffer.fg, self.buffer.bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goLeft(self: *Self) void {
|
|
||||||
self.current = if (self.current == 0) self.list.items.len - 1 else self.current - 1;
|
|
||||||
|
|
||||||
if (self.change_item_fn) |change_item_fn| {
|
|
||||||
@call(.auto, change_item_fn, .{ self.list.items[self.current], self.change_item_arg });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goRight(self: *Self) void {
|
|
||||||
self.current = if (self.current == self.list.items.len - 1) 0 else self.current + 1;
|
|
||||||
|
|
||||||
if (self.change_item_fn) |change_item_fn| {
|
|
||||||
@call(.auto, change_item_fn, .{ self.list.items[self.current], self.change_item_arg });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue