90 Commits

Author SHA1 Message Date
AnErrupTion
42393f46d1 Only support Ly v1.1.x in bug reports
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-30 19:14:25 +02:00
AnErrupTion
fa0d8c509d Merge pull request 'Update readme.md' (#777) from a1e5Term-rf-ln/ly:new1 into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/777
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-05-30 17:49:20 +02:00
AnErrupTion
b42953fd7e Update screenshot
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-30 17:39:44 +02:00
B P
11d9cf8b71 Update readme.md 2025-05-30 18:35:46 +03:00
AnErrupTion
67a4dd8f9d Rewrite DOOM animation (fixes #692)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-30 16:11:14 +02:00
AnErrupTion
cedb7a3b02 Fix TTY not being cleared sometimes (closes #696)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-30 14:18:29 +02:00
AnErrupTion
02729cce21 Add brightnessctl to runtime dependency list
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-30 14:17:41 +02:00
AnErrupTion
d9204131aa Fix brightness up key not working (closes #763)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-30 09:24:07 +02:00
AnErrupTion
e90cf40e5b Update termbox2
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-29 23:52:58 +02:00
AnErrupTion
b0395ef103 Merge pull request 'Update Polish translation' (#758) from Bluudek/ly:pl_lang_update into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/758
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-05-29 23:13:27 +02:00
AnErrupTion
692e7265e3 Merge pull request 'Added dwm and gnome on Supported X11 Environments' (#753) from ManogyaDahal/ly:manogya/update_readme_supported_device into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/753
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-05-29 22:49:44 +02:00
Bluudek
732888fd94 Remove invisible Unicode characters 2025-05-08 00:16:14 +02:00
Bluudek
71a06e13a6 Update Polish phrases 2025-05-08 00:12:50 +02:00
Bluudek
f74c4e92a9 Update Polish missing messages 2025-05-07 23:31:31 +02:00
ManogyaDahal
555e72a388 Updae: Added dwm in Readme 2025-04-28 14:18:12 +05:45
AnErrupTion
a1b38188d0 Merge pull request 'Add Arabic Translation' (#745) from MohamedAAbdallah/ly:master into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/745
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-04-05 07:37:04 +00:00
Mohamed A. Abdallah
751d31cae2 Update Arabic error messages for errors 2025-03-28 23:26:17 +02:00
Mohamed A. Abdallah
c6a3223a03 Update Arabic localization for actions 2025-03-28 23:10:09 +02:00
Mohamed A. Abdallah
d2803194f3 Add Arabic localization file 2025-03-28 22:57:13 +02:00
AnErrupTion
4345a99c02 Merge pull request 'Support specifying multiple directories to crawl for sessions' (#744) from jack-avery/ly:multiple_session_dirs into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/744
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-03-28 16:57:15 +00:00
jack-avery
fecc688418 snake case 2025-03-28 11:01:33 -04:00
jack-avery
b7e37ce1b7 update config comments 2025-03-28 11:00:47 -04:00
jack-avery
be5a68dd1d allow specify multiple dirs 2025-03-28 00:47:12 -04:00
AnErrupTion
622303d150 Merge pull request 'Separate Wayland/X11 environments in README' (#742) from Chiron8/ly:master into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/742
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-03-20 18:20:20 +00:00
Chiron8
7747b27f9d Update readme.md 2025-03-20 13:08:13 +00:00
Chiron8
585ca5f0da Update readme.md 2025-03-20 08:24:25 +00:00
Chiron8
c624c35ac7 Update readme.md 2025-03-17 16:25:06 +00:00
Chiron8
beb6d04c58 Update readme.md 2025-03-17 16:19:43 +00:00
Chiron8
32793bcfeb Update readme.md 2025-03-17 16:18:51 +00:00
AnErrupTion
9ded9fd765 Remove use of deprecated aliases/types + use upstream zigini
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-16 22:45:46 +01:00
AnErrupTion
13ba52319c Clean termbox2 usage + fix animation bug
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-16 11:40:27 +01:00
AnErrupTion
1672d4a9ec Make main code less directly dependent on termbox2
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-16 11:17:38 +01:00
AnErrupTion
e0ed1b4eb1 Add animation framework
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-16 10:54:06 +01:00
AnErrupTion
fa0748ead2 Remove unused valgrind file
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-16 09:13:01 +01:00
AnErrupTion
86ea38f460 Split session crawling from TUI component
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-16 00:24:56 +01:00
AnErrupTion
9efb734fd5 Show Ly version string at top-left
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-13 13:27:18 +01:00
AnErrupTion
5f52063835 Remove duplicate information in README
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-12 08:00:42 +01:00
AnErrupTion
548fa210c1 Update README with new build commands
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-12 07:58:37 +01:00
AnErrupTion
75975ea301 Add Niri to tested WM list
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-12 07:48:38 +01:00
AnErrupTion
8fa6b2cec9 Improve build system commands
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-07 11:03:45 +01:00
AnErrupTion
4dcef65b1c Fix X11 session logout
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 23:32:23 +01:00
AnErrupTion
1eca889e45 Confirm LeftWM support (closes #164)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 22:57:24 +01:00
AnErrupTion
ac1d828a5f Stop spamming err_console_dev when first time didn't work
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 22:02:01 +01:00
AnErrupTion
92845268af Add option to allow empty password or not (closes #577)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 22:00:46 +01:00
AnErrupTion
6504cd0209 Mention OS in issue template
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 21:51:17 +01:00
AnErrupTion
a058a81ec9 Use proper input text box in issue template
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 21:48:40 +01:00
AnErrupTion
c6db79b873 Fix issue template
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 21:48:03 +01:00
AnErrupTion
932c751ac2 Stop redirecting X11 output to session log (closes #693, #688)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 21:46:40 +01:00
AnErrupTion
4599654398 Update issue template for bugs
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 21:41:28 +01:00
AnErrupTion
e19a23b54c Allow using up to a UTF-32 codepoint as a password asterisk (closes #715)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 21:16:24 +01:00
AnErrupTion
f2ca72eace Add (incomplete) Chinese translation, thanks @eonun! (closes #194)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 21:08:05 +01:00
AnErrupTion
4e859e56cb Allow modifying DOOM animation fire colors (closes #239)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 20:40:53 +01:00
AnErrupTion
78d64ad2a7 Possibly fix .Xresources not being loaded (closes #600)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 20:06:28 +01:00
AnErrupTion
d80ec8fd1f Allow changing matrix animation min/max codepoints (closes #615)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 19:48:11 +01:00
AnErrupTion
d12fa27168 Added new error and updated French translation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 19:41:05 +01:00
AnErrupTion
593a775148 Use unsigned integers only in Matrix animation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 19:36:42 +01:00
AnErrupTion
6079c01a4b Allow disabling the brightness control commands (closes #664)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 19:16:47 +01:00
AnErrupTion
9c79137c9f Remove all deprecated calls to tb_cell_buffer()
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 15:42:33 +01:00
AnErrupTion
9168266cca Don't shutdown termbox2 if authentication fails
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 15:29:56 +01:00
AnErrupTion
6cb102257c Fix Doom & Matrix animation + bug in migrator
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 15:27:38 +01:00
AnErrupTion
55abc4d7f1 Decouple TerminalBuffer and auth from Config
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 14:32:05 +01:00
AnErrupTion
f013af0dde Fix segmentation fault when using color mix after auth
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 14:31:11 +01:00
AnErrupTion
973d8fe120 Don't dynamically allocate color strings
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 13:23:47 +01:00
AnErrupTion
3e6d7a1b3b Forgot a file lol
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 13:09:19 +01:00
AnErrupTion
0c69e0412c Don't make errors blink by default
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 13:08:50 +01:00
AnErrupTion
d0ccaa4d69 Enable true color output (closes #705)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 12:47:28 +01:00
AnErrupTion
a766dc2b9c Update termbox2
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 11:42:19 +01:00
AnErrupTion
f54657432a Don't set XDG_CURRENT_DESKTOP and XDG_SESSION_DESKTOP if they're empty (closes #702)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 22:50:09 +01:00
AnErrupTion
87503367e9 Try to create /etc/pam.d and /usr/bin everytime when installing
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 22:36:17 +01:00
AnErrupTion
85e071a60a Code style changes in build.zig
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 22:07:18 +01:00
AnErrupTion
24599368df Minor changes in color mix animation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 22:06:56 +01:00
AnErrupTion
d5bc6aab24 Merge pull request 'Color mix animation' (#724) from winlith/colormix into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/724
Reviewed-by: AnErrupTion <anerruption@noreply.codeberg.org>
2025-03-05 20:54:48 +00:00
AnErrupTion
c9a416b8a2 Merge branch 'master' into winlith/colormix 2025-03-05 20:53:53 +00:00
AnErrupTion
15c2b19371 Merge pull request 'Make language files uniform' (#704) from moritz-reinel/03_uniform-lang-files into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/704
Reviewed-by: AnErrupTion <anerruption@noreply.codeberg.org>
2025-03-05 20:28:57 +00:00
AnErrupTion
142073c362 Merge branch 'master' into moritz-reinel/03_uniform-lang-files 2025-03-05 20:26:01 +00:00
AnErrupTion
514931b486 Merge pull request 'fix: don't include dest_dir in config_dir' (#728) from villamorrd/master into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/728
Reviewed-by: AnErrupTion <anerruption@noreply.codeberg.org>
2025-03-05 19:53:54 +00:00
AnErrupTion
50e9e912e0 Merge branch 'master' into villamorrd/master 2025-03-05 19:52:09 +00:00
AnErrupTion
895edaf904 Update README to only include Zig 0.14.0
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 19:43:02 +01:00
AnErrupTion
bebccf4d5a Support Zig 0.14.0 only (with 1 downstream dependency fork)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 19:42:00 +01:00
AnErrupTion
346a614ba0 Mention Codeberg in the README
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 02:03:40 +00:00
villamorrd
d171634f9b fix: don't include dest_dir in config_dir
This should fix $CONFIG_DIRECTORY including the installation directory in its path.
2025-02-01 20:58:52 +08:00
winlith
f5f7422d82 color mix animation 2025-01-17 22:37:48 +01:00
Moritz Reinel
117eccd65a rename language normalizer script 2024-10-26 13:19:33 +02:00
Moritz Reinel
b6726a76c6 refactored some variables + added comments 2024-10-26 13:09:30 +02:00
Moritz Reinel
0dea19c8db remove bash version of script 2024-10-21 19:43:33 +02:00
Moritz Reinel
7a82b51ac5 Revert "remove python version of lang script"
This reverts commit 7a1fce660c.
2024-10-21 19:42:57 +02:00
Moritz Reinel
fe36879dfb added note to Lang.zig and updated lang script to not process the comment 2024-10-12 22:07:29 +02:00
Moritz Reinel
47ebe641d9 remove necessity for temp file 2024-10-12 21:51:50 +02:00
Moritz Reinel
7a1fce660c remove python version of lang script 2024-10-12 21:27:19 +02:00
Moritz Reinel
06e283961d add helper script for keeping lang files in sync with Lang.zig 2024-10-12 21:26:31 +02:00
49 changed files with 2809 additions and 1232 deletions

View File

@@ -15,7 +15,7 @@ body:
id: version
attributes:
label: Ly version
description: The output of `ly --version`
description: The output of `ly --version`. Please note that only Ly v1.1.0 and above are supported.
placeholder: 1.1.0-dev.12+2b0301c
validations:
required: true
@@ -33,6 +33,13 @@ body:
description: What did you expect to happen instead?
validations:
required: true
- type: input
id: desktop
attributes:
label: OS + Desktop environment/Window manager
description: Which OS and DE (or WM) did you use when observing the problem?
validations:
required: true
- type: textarea
id: reproduction
attributes:
@@ -50,5 +57,12 @@ body:
id: logs
attributes:
label: Relevant logs
description: Please copy and paste any relevant logs, error messages or any other output. This will be automatically formatted into code, so no need for backticks. Screenshots are accepted if they make life easier for you.
description: |
Please copy and paste any relevant logs, error messages or any other output. This will be automatically formatted into code, so no need for backticks. Screenshots are accepted if they make life easier for you.
If you're using the latest code on master (for v1.1.0), including your session log (found at /var/log/ly-session.log unless modified) is a good idea. (But make sure it's relevant!)
render: shell
- type: textarea
id: moreinfo
attributes:
label: Additional information
description: If you have any additional information that might be helpful in reproducing the problem, please provide it here.

BIN
.github/screenshot.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 43 KiB

298
build.zig
View File

@@ -2,8 +2,15 @@ const std = @import("std");
const builtin = @import("builtin");
const PatchMap = std.StringHashMap([]const u8);
const InitSystem = enum {
systemd,
openrc,
runit,
s6,
dinit,
};
const min_zig_string = "0.12.0";
const min_zig_string = "0.14.0";
const current_zig = builtin.zig_version;
// Implementing zig version detection through compile time
@@ -20,18 +27,15 @@ var dest_directory: []const u8 = undefined;
var config_directory: []const u8 = undefined;
var prefix_directory: []const u8 = undefined;
var executable_name: []const u8 = undefined;
var init_system: InitSystem = undefined;
var default_tty_str: []const u8 = undefined;
const ProgressNode = if (current_zig.minor == 12) *std.Progress.Node else std.Progress.Node;
pub fn build(b: *std.Build) !void {
dest_directory = b.option([]const u8, "dest_directory", "Specify a destination directory for installation") orelse "";
config_directory = b.option([]const u8, "config_directory", "Specify a default config directory (default is /etc). This path gets embedded into the binary") orelse "/etc";
prefix_directory = b.option([]const u8, "prefix_directory", "Specify a default prefix directory (default is /usr)") orelse "/usr";
executable_name = b.option([]const u8, "name", "Specify installed executable file name (default is ly)") orelse "ly";
const bin_directory = try b.allocator.dupe(u8, config_directory);
config_directory = try std.fs.path.join(b.allocator, &[_][]const u8{ dest_directory, config_directory });
init_system = b.option(InitSystem, "init_system", "Specify the target init system (default is systemd)") orelse .systemd;
const build_options = b.addOptions();
const version_str = try getVersionStr(b, "ly", ly_version);
@@ -40,7 +44,7 @@ pub fn build(b: *std.Build) !void {
default_tty_str = try std.fmt.allocPrint(b.allocator, "{d}", .{default_tty});
build_options.addOption([]const u8, "config_directory", bin_directory);
build_options.addOption([]const u8, "config_directory", config_directory);
build_options.addOption([]const u8, "prefix_directory", prefix_directory);
build_options.addOption([]const u8, "version", version_str);
build_options.addOption(u8, "tty", default_tty);
@@ -75,6 +79,7 @@ pub fn build(b: *std.Build) !void {
.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);
@@ -89,57 +94,24 @@ pub fn build(b: *std.Build) !void {
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
const installexe_step = b.step("installexe", "Install Ly");
installexe_step.makeFn = ExeInstaller(true).make;
const installexe_step = b.step("installexe", "Install Ly and the selected init system service");
installexe_step.makeFn = Installer(true).make;
installexe_step.dependOn(b.getInstallStep());
const installnoconf_step = b.step("installnoconf", "Install Ly without its configuration file");
installnoconf_step.makeFn = ExeInstaller(false).make;
const installnoconf_step = b.step("installnoconf", "Install Ly and the selected init system service, but not the configuration file");
installnoconf_step.makeFn = Installer(false).make;
installnoconf_step.dependOn(b.getInstallStep());
const installsystemd_step = b.step("installsystemd", "Install the Ly systemd service");
installsystemd_step.makeFn = ServiceInstaller(.Systemd).make;
installsystemd_step.dependOn(installexe_step);
const uninstallexe_step = b.step("uninstallexe", "Uninstall Ly and remove the selected init system service");
uninstallexe_step.makeFn = Uninstaller(true).make;
const installopenrc_step = b.step("installopenrc", "Install the Ly openrc service");
installopenrc_step.makeFn = ServiceInstaller(.Openrc).make;
installopenrc_step.dependOn(installexe_step);
const installrunit_step = b.step("installrunit", "Install the Ly runit service");
installrunit_step.makeFn = ServiceInstaller(.Runit).make;
installrunit_step.dependOn(installexe_step);
const installs6_step = b.step("installs6", "Install the Ly s6 service");
installs6_step.makeFn = ServiceInstaller(.S6).make;
installs6_step.dependOn(installexe_step);
const installdinit_step = b.step("installdinit", "Install the Ly dinit service");
installdinit_step.makeFn = ServiceInstaller(.Dinit).make;
installdinit_step.dependOn(installexe_step);
const uninstallall_step = b.step("uninstallall", "Uninstall Ly and all services");
uninstallall_step.makeFn = uninstallall;
const uninstallnoconf_step = b.step("uninstallnoconf", "Uninstall Ly and remove the selected init system service, but keep the configuration directory");
uninstallnoconf_step.makeFn = Uninstaller(false).make;
}
pub fn ExeInstaller(install_conf: bool) type {
pub fn Installer(install_config: bool) type {
return struct {
pub fn make(step: *std.Build.Step, _: ProgressNode) !void {
try install_ly(step.owner.allocator, install_conf);
}
};
}
const InitSystem = enum {
Systemd,
Openrc,
Runit,
S6,
Dinit,
};
pub fn ServiceInstaller(comptime init_system: InitSystem) type {
return struct {
pub fn make(step: *std.Build.Step, _: ProgressNode) !void {
pub fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
const allocator = step.owner.allocator;
var patch_map = PatchMap.init(allocator);
@@ -150,96 +122,31 @@ pub fn ServiceInstaller(comptime init_system: InitSystem) type {
try patch_map.put("$PREFIX_DIRECTORY", prefix_directory);
try patch_map.put("$EXECUTABLE_NAME", executable_name);
switch (init_system) {
.Systemd => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/lib/systemd/system" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const patched_service = try patchFile(allocator, "res/ly.service", patch_map);
try installText(patched_service, service_dir, service_path, "ly.service", .{ .mode = 0o644 });
},
.Openrc => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const patched_service = try patchFile(allocator, "res/ly-openrc", patch_map);
try installText(patched_service, service_dir, service_path, executable_name, .{ .mode = 0o755 });
},
.Runit => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/sv/ly" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const supervise_path = try std.fs.path.join(allocator, &[_][]const u8{ service_path, "supervise" });
const patched_conf = try patchFile(allocator, "res/ly-runit-service/conf", patch_map);
try installText(patched_conf, service_dir, service_path, "conf", .{});
try installFile("res/ly-runit-service/finish", service_dir, service_path, "finish", .{ .override_mode = 0o755 });
const patched_run = try patchFile(allocator, "res/ly-runit-service/run", patch_map);
try installText(patched_run, service_dir, service_path, "run", .{ .mode = 0o755 });
try std.fs.cwd().symLink("/run/runit/supervise.ly", supervise_path, .{});
std.debug.print("info: installed symlink /run/runit/supervise.ly\n", .{});
},
.S6 => {
const admin_service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/adminsv/default/contents.d" });
std.fs.cwd().makePath(admin_service_path) catch {};
var admin_service_dir = std.fs.cwd().openDir(admin_service_path, .{}) catch unreachable;
defer admin_service_dir.close();
const file = try admin_service_dir.createFile("ly-srv", .{});
file.close();
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/sv/ly-srv" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const patched_run = try patchFile(allocator, "res/ly-s6/run", patch_map);
try installText(patched_run, service_dir, service_path, "run", .{ .mode = 0o755 });
try installFile("res/ly-s6/type", service_dir, service_path, "type", .{});
},
.Dinit => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/dinit.d" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const patched_service = try patchFile(allocator, "res/ly-dinit", patch_map);
try installText(patched_service, service_dir, service_path, "ly", .{});
},
}
try install_ly(allocator, patch_map, install_config);
try install_service(allocator, patch_map);
}
};
}
fn install_ly(allocator: std.mem.Allocator, install_config: bool) !void {
const ly_config_directory = try std.fs.path.join(allocator, &[_][]const u8{ config_directory, "/ly" });
fn install_ly(allocator: std.mem.Allocator, patch_map: PatchMap, install_config: bool) !void {
const ly_config_directory = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly" });
std.fs.cwd().makePath(ly_config_directory) catch {
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_config_directory});
};
const ly_lang_path = try std.fs.path.join(allocator, &[_][]const u8{ config_directory, "/ly/lang" });
const ly_lang_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/lang" });
std.fs.cwd().makePath(ly_lang_path) catch {
std.debug.print("warn: {s} already exists as a directory.\n", .{config_directory});
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" });
if (!std.mem.eql(u8, dest_directory, "")) {
std.fs.cwd().makePath(exe_path) catch {
std.fs.cwd().makePath(exe_path) catch {
if (!std.mem.eql(u8, dest_directory, "")) {
std.debug.print("warn: {s} already exists as a directory.\n", .{exe_path});
};
}
}
};
var executable_dir = std.fs.cwd().openDir(exe_path, .{}) catch unreachable;
defer executable_dir.close();
@@ -252,26 +159,12 @@ fn install_ly(allocator: std.mem.Allocator, install_config: bool) !void {
defer config_dir.close();
if (install_config) {
var patch_map = PatchMap.init(allocator);
defer patch_map.deinit();
try patch_map.put("$DEFAULT_TTY", default_tty_str);
try patch_map.put("$CONFIG_DIRECTORY", config_directory);
try patch_map.put("$PREFIX_DIRECTORY", prefix_directory);
const patched_config = try patchFile(allocator, "res/config.ini", patch_map);
try installText(patched_config, config_dir, ly_config_directory, "config.ini", .{});
}
{
var patch_map = PatchMap.init(allocator);
defer patch_map.deinit();
try patch_map.put("$CONFIG_DIRECTORY", config_directory);
const patched_setup = try patchFile(allocator, "res/setup.sh", patch_map);
try installText(patched_setup, config_dir, ly_config_directory, "setup.sh", .{ .mode = 0o755 });
}
const patched_setup = try patchFile(allocator, "res/setup.sh", patch_map);
try installText(patched_setup, config_dir, ly_config_directory, "setup.sh", .{ .mode = 0o755 });
}
{
@@ -298,11 +191,11 @@ fn install_ly(allocator: std.mem.Allocator, install_config: bool) !void {
{
const pam_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/pam.d" });
if (!std.mem.eql(u8, dest_directory, "")) {
std.fs.cwd().makePath(pam_path) catch {
std.fs.cwd().makePath(pam_path) catch {
if (!std.mem.eql(u8, dest_directory, "")) {
std.debug.print("warn: {s} already exists as a directory.\n", .{pam_path});
};
}
}
};
var pam_dir = std.fs.cwd().openDir(pam_path, .{}) catch unreachable;
defer pam_dir.close();
@@ -311,26 +204,107 @@ fn install_ly(allocator: std.mem.Allocator, install_config: bool) !void {
}
}
pub fn uninstallall(step: *std.Build.Step, _: ProgressNode) !void {
const allocator = step.owner.allocator;
fn install_service(allocator: std.mem.Allocator, patch_map: PatchMap) !void {
switch (init_system) {
.systemd => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/lib/systemd/system" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
try deleteTree(allocator, config_directory, "/ly", "ly config directory not found");
const patched_service = try patchFile(allocator, "res/ly.service", patch_map);
try installText(patched_service, service_dir, service_path, "ly.service", .{ .mode = 0o644 });
},
.openrc => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin/", executable_name });
var success = true;
std.fs.cwd().deleteFile(exe_path) catch {
std.debug.print("warn: ly executable not found\n", .{});
success = false;
const patched_service = try patchFile(allocator, "res/ly-openrc", patch_map);
try installText(patched_service, service_dir, service_path, executable_name, .{ .mode = 0o755 });
},
.runit => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/sv/ly" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const supervise_path = try std.fs.path.join(allocator, &[_][]const u8{ service_path, "supervise" });
const patched_conf = try patchFile(allocator, "res/ly-runit-service/conf", patch_map);
try installText(patched_conf, service_dir, service_path, "conf", .{});
try installFile("res/ly-runit-service/finish", service_dir, service_path, "finish", .{ .override_mode = 0o755 });
const patched_run = try patchFile(allocator, "res/ly-runit-service/run", patch_map);
try installText(patched_run, service_dir, service_path, "run", .{ .mode = 0o755 });
try std.fs.cwd().symLink("/run/runit/supervise.ly", supervise_path, .{});
std.debug.print("info: installed symlink /run/runit/supervise.ly\n", .{});
},
.s6 => {
const admin_service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/adminsv/default/contents.d" });
std.fs.cwd().makePath(admin_service_path) catch {};
var admin_service_dir = std.fs.cwd().openDir(admin_service_path, .{}) catch unreachable;
defer admin_service_dir.close();
const file = try admin_service_dir.createFile("ly-srv", .{});
file.close();
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/sv/ly-srv" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const patched_run = try patchFile(allocator, "res/ly-s6/run", patch_map);
try installText(patched_run, service_dir, service_path, "run", .{ .mode = 0o755 });
try installFile("res/ly-s6/type", service_dir, service_path, "type", .{});
},
.dinit => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/dinit.d" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const patched_service = try patchFile(allocator, "res/ly-dinit", patch_map);
try installText(patched_service, service_dir, service_path, "ly", .{});
},
}
}
pub fn Uninstaller(uninstall_config: bool) type {
return struct {
pub fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
const allocator = step.owner.allocator;
if (uninstall_config) {
try deleteTree(allocator, config_directory, "/ly", "ly config directory not found");
}
const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ prefix_directory, "/bin/", executable_name });
var success = true;
std.fs.cwd().deleteFile(exe_path) catch {
std.debug.print("warn: ly executable not found\n", .{});
success = false;
};
if (success) std.debug.print("info: deleted {s}\n", .{exe_path});
try deleteFile(allocator, config_directory, "/pam.d/ly", "ly pam file not found");
switch (init_system) {
.systemd => try deleteFile(allocator, prefix_directory, "/lib/systemd/system/ly.service", "systemd service not found"),
.openrc => try deleteFile(allocator, config_directory, "/init.d/ly", "openrc service not found"),
.runit => try deleteTree(allocator, config_directory, "/sv/ly", "runit service not found"),
.s6 => {
try deleteTree(allocator, config_directory, "/s6/sv/ly-srv", "s6 service not found");
try deleteFile(allocator, config_directory, "/s6/adminsv/default/contents.d/ly-srv", "s6 admin service not found");
},
.dinit => try deleteFile(allocator, config_directory, "/dinit.d/ly", "dinit service not found"),
}
}
};
if (success) std.debug.print("info: deleted {s}\n", .{exe_path});
try deleteFile(allocator, config_directory, "/pam.d/ly", "ly pam file not found");
try deleteFile(allocator, prefix_directory, "/lib/systemd/system/ly.service", "systemd service not found");
try deleteFile(allocator, config_directory, "/init.d/ly", "openrc service not found");
try deleteTree(allocator, config_directory, "/sv/ly", "runit service not found");
try deleteTree(allocator, config_directory, "/s6/sv/ly-srv", "s6 service not found");
try deleteFile(allocator, config_directory, "/s6/adminsv/default/contents.d/ly-srv", "s6 admin service not found");
try deleteFile(allocator, config_directory, "/dinit.d/ly", "dinit service not found");
}
fn getVersionStr(b: *std.Build, name: []const u8, version: std.SemanticVersion) ![]const u8 {

View File

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

File diff suppressed because it is too large Load Diff

125
readme.md
View File

@@ -1,12 +1,14 @@
# Ly - a TUI display manager
## Development is now continuing on [Codeberg](https://codeberg.org/AnErrupTion/ly), with the [GitHub](https://github.com/fairyglade/ly) repository becoming a mirror. Issues & pull requests on GitHub will be ignored from now on.
![Ly screenshot](.github/screenshot.png "Ly screenshot")
Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD.
## Dependencies
- Compile-time:
- zig >=0.12.0
- zig 0.14.0
- libc
- pam
- xcb (optional, required by default; needed for X11 support)
@@ -14,6 +16,7 @@ Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD.
- xorg
- xorg-xauth
- shutdown
- brightnessctl
### Debian
```
@@ -30,31 +33,10 @@ It is recommended to add a rule for Ly as it currently does not ship one.
## Support
The following desktop environments were tested with success:
- awesome
- bspwm
- budgie
- cinnamon
- cosmic
- deepin
- dwl
- dwm
- enlightenment
- gnome
- hyprland
- i3
- kde
- labwc
- lxde
- lxqt
- mate
- maxx
- pantheon
- qtile
- spectrwm
- sway
- windowmaker
- xfce
- xmonad
[Wayland Environments](#supported-wayland-environments)
[X11 Environments](#supported-x11-environments)
Ly should work with any X desktop environment, and provides
basic wayland support (sway works very well, for example).
@@ -68,7 +50,7 @@ changing the source code won't be necessary :)
## Cloning and Compiling
Clone the repository
```
$ git clone https://github.com/fairyglade/ly
$ git clone https://codeberg.org/AnErrupTion/ly
```
Change the directory to ly
@@ -82,14 +64,26 @@ $ zig build
```
Test in the configured tty (tty2 by default)
or a terminal emulator (but desktop environments won't start)
or a terminal emulator (but authentication won't work)
```
# zig build run
$ zig build run
```
Install Ly and the provided systemd service file
**Important**: Running Ly in a terminal emulator as root is *not* recommended. If you
want to properly test Ly, please enable its service (as described below) and reboot
your machine.
Install Ly for systemd-based systems (the default)
```
# zig build installsystemd
# zig build installexe
```
Instead of DISPLAY_MANAGER you need to add your DM:
- gdm.service
- sddm.service
- lightdm.service
```
# systemctl disable DISPLAY_MANAGER
```
Enable the service
@@ -110,7 +104,7 @@ Clone, compile and test.
Install Ly and the provided OpenRC service
```
# zig build installopenrc
# zig build installexe -Dinit_system=openrc
```
Enable the service
@@ -130,7 +124,7 @@ then you have to disable getty, so it doesn't respawn on top of ly
### runit
```
# zig build installrunit
# zig build installexe -Dinit_system=runit
# ln -s /etc/sv/ly /var/service/
```
@@ -152,7 +146,7 @@ you should disable the agetty-tty2 service like this:
### s6
```
# zig build installs6
# zig build installexe -Dinit_system=s6
```
Then, edit `/etc/s6/config/ttyX.conf` and set `SPAWN="no"`, where X is the TTY ID (e.g. `2`).
@@ -167,7 +161,7 @@ Finally, enable the service:
### dinit
```
# zig build installdinit
# zig build installexe -Dinit_system=dinit
# dinitctl enable ly
```
@@ -176,23 +170,19 @@ In addition to the steps above, you will also have to keep a TTY free within `/e
To do that, change `ACTIVE_CONSOLES` so that the tty that ly should use in `/etc/ly/config.ini` is free.
### Updating
You can also install Ly without copying the system service and the configuration file. That's
called *updating*. To update, simply run:
You can also install Ly without overrding the current configuration file. That's called
*updating*. To update, simply run:
```
# zig build installnoconf
```
If you want to also copy the default config file (but still not the system service), run:
```
# zig build installexe
```
You can, of course, still select the init system of your choice when using this command.
## Arch Linux Installation
You can install ly from the [`[extra]` repos](https://archlinux.org/packages/extra/x86_64/ly/):
```
$ sudo pacman -S ly
# pacman -S ly
```
## Gentoo Installation
@@ -239,17 +229,46 @@ On Arch Linux, the example .xinitrc (/etc/X11/xinit/xinitrc) starts like this:
```
## Tips
The numlock and capslock state is printed in the top-right corner.
Use the F1 and F2 keys to respectively shutdown and reboot.
Take a look at your .xsession if X doesn't start, as it can interfere
(this file is launched with X to configure the display properly).
- The numlock and capslock state is printed in the top-right corner.
- Use the F1 and F2 keys to respectively shutdown and reboot.
- Take a look at your .xsession if X doesn't start, as it can interfere
(this file is launched with X to configure the display properly).
## Supported Wayland Environments
- budgie
- cosmic
- deepin
- enlightenment
- gnome
- hyprland
- kde
- labwc
- niri
- pantheon
- sway
- weston
## Supported X11 Environments
- awesome
- bspwm
- budgie
- cinnamon
- dwm
- enlightenment
- gnome
- kde
- leftwm
- lxde
- mate
- maxx
- pantheon
- qwm
- spectrwm
- windowmaker
- xfce
- xmonad
## PSX DOOM fire animation
To enable the famous PSX DOOM fire described by [Fabien Sanglard](http://fabiensanglard.net/doom_fire_psx/index.html),
just set `animation = doom` in `/etc/ly/config.ini`. You may also
disable the main box borders with `hide_borders = true`.
## Additional Information
The name "Ly" is a tribute to the fairy from the game Rayman.
Ly was tested by oxodao, who is some seriously awesome dude.

View File

@@ -1,54 +1,38 @@
# The color settings in Ly take a digit 0-8 corresponding to:
#define TB_DEFAULT 0x00
#define TB_BLACK 0x01
#define TB_RED 0x02
#define TB_GREEN 0x03
#define TB_YELLOW 0x04
#define TB_BLUE 0x05
#define TB_MAGENTA 0x06
#define TB_CYAN 0x07
#define TB_WHITE 0x08
# The default color varies, but usually it makes the background black and the foreground white.
# You can also combine these colors with the following style attributes using bitwise OR:
#define TB_BOLD 0x0100
#define TB_UNDERLINE 0x0200
#define TB_REVERSE 0x0400
#define TB_ITALIC 0x0800
#define TB_BLINK 0x1000
#define TB_HI_BLACK 0x2000
#define TB_BRIGHT 0x4000
#define TB_DIM 0x8000
# For example, to set the foreground color to red and bold, you would do 0x02 | 0x0100 = 0x0102.
# Note that you must pre-calculate the value because Ly doesn't parse bitwise OR operations in its config.
#
# Moreover, to set the VT color palette, you are encouraged to use another tool such as
# mkinitcpio-colors (https://github.com/evanpurkhiser/mkinitcpio-colors). Note that the color palette defined with
# mkinitcpio-colors takes 16 colors (0-15), only values 0-8 are valid with Ly and these values do not correspond
# exactly. For instance, in defining palettes with mkinitcpio-colors, the order is black, dark red, dark green, brown, dark
# blue, dark purple, dark cyan, light gray, dark gray, bright red, bright green, yellow, bright blue, bright purple, bright
# cyan, and white, indexed in that order 0 through 15. For example, the color defined for white (indexed at 15 in the mkinitcpio
# config) will be used by Ly for fg = 0x0008.
# Ly supports 24-bit true color with styling, which means each color is a 32-bit value.
# The format is 0xSSRRGGBB, where SS is the styling, RR is red, GG is green, and BB is blue.
# Here are the possible styling options:
#define TB_BOLD 0x01000000
#define TB_UNDERLINE 0x02000000
#define TB_REVERSE 0x04000000
#define TB_ITALIC 0x08000000
#define TB_BLINK 0x10000000
#define TB_HI_BLACK 0x20000000
#define TB_BRIGHT 0x40000000
#define TB_DIM 0x80000000
# Programmatically, you'd apply them using the bitwise OR operator (|), but because Ly's
# configuration doesn't support using it, you have to manually compute the color value.
# Note that, if you want to use the default color value of the terminal, you can use the
# special value 0x00000000. This means that, if you want to use black, you *must* use
# the styling option TB_HI_BLACK (the RGB values are ignored when using this option).
# Allow empty password or not when authenticating
allow_empty_password = true
# The active animation
# none -> Nothing
# doom -> PSX DOOM fire
# matrix -> CMatrix
# none -> Nothing
# doom -> PSX DOOM fire
# matrix -> CMatrix
# colormix -> Color mixing shader
animation = none
# The minimum time between animation frames
# 32 -> ~30fps
# 16 -> ~60fps
# 8 -> ~120fps
# 6 -> ~144fps
# 4 -> ~240fps
animation_refresh_ms = 16
# Stop the animation after some time
# 0 -> Run forever
# 1..2e12 -> Stop the animation after this many seconds
animation_timeout_sec = 0
# The character used to mask the password
# You can either type it directly as a UTF-8 character (like *), or use a UTF-32
# codepoint (for example 0x2022 for a bullet point)
# If null, the password will be hidden
# Note: you can use a # by escaping it like so: \#
asterisk = *
@@ -57,7 +41,7 @@ asterisk = *
auth_fails = 10
# Background color id
bg = 0x0000
bg = 0x00000000
# Change the state and language of the big clock
# none -> Disabled (default)
@@ -70,7 +54,7 @@ bigclock = none
blank_box = true
# Border foreground color id
border_fg = 0x0008
border_fg = 0x00FFFFFF
# Title to show at the top of the main box
# If set to null, none will be shown
@@ -79,13 +63,13 @@ box_title = null
# Brightness increase command
brightness_down_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q s 10%-
# Brightness decrease key
# Brightness decrease key, or null to disable
brightness_down_key = F5
# Brightness increase command
brightness_up_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q s +10%
# Brightness increase key
# Brightness increase key, or null to disable
brightness_up_key = F6
# Erase password input on failure
@@ -96,7 +80,24 @@ clear_password = false
clock = null
# CMatrix animation foreground color id
cmatrix_fg = 0x0003
cmatrix_fg = 0x0000FF00
# CMatrix animation minimum codepoint. It uses a 16-bit integer
# For Japanese characters for example, you can use 0x3000 here
cmatrix_min_codepoint = 0x21
# CMatrix animation maximum codepoint. It uses a 16-bit integer
# For Japanese characters for example, you can use 0x30FF here
cmatrix_max_codepoint = 0x7B
# Color mixing animation first color id
colormix_col1 = 0x00FF0000
# Color mixing animation second color id
colormix_col2 = 0x000000FF
# Color mixing animation third color id
colormix_col3 = 0x20000000
# Console path
console_dev = /dev/console
@@ -105,15 +106,24 @@ console_dev = /dev/console
# Available inputs: info_line, session, login, password
default_input = login
# DOOM animation top color (low intensity flames)
doom_top_color = 0x00FF0000
# DOOM animation middle color (medium intensity flames)
doom_middle_color = 0x00FFFF00
# DOOM animation bottom color (high intensity flames)
doom_bottom_color = 0x00FFFFFF
# Error background color id
error_bg = 0x0000
error_bg = 0x00000000
# Error foreground color id
# Default is red and bold: TB_RED | TB_BOLD
error_fg = 0x0102
# Default is red and bold
error_fg = 0x01FF0000
# Foreground color id
fg = 0x0008
fg = 0x00FFFFFF
# Remove main box borders
hide_borders = false
@@ -176,9 +186,10 @@ save = true
service_name = ly
# Session log file path
# This will contain stdout and stderr of X11 and Wayland sessions
# This will contain stdout and stderr of Wayland sessions
# By default it's saved in the user's home directory
# Note: this file won't be used in a shell session (due to the need of stdout and stderr)
# Important: due to technical limitations, X11 and shell sessions aren't supported, which
# means you won't get any logs from those sessions
session_log = ly-session.log
# Setup command
@@ -211,6 +222,8 @@ vi_default_mode = normal
vi_mode = false
# Wayland desktop environments
# You can specify multiple directories,
# e.g. /usr/share/wayland-sessions:/usr/local/share/wayland-sessions
waylandsessions = $PREFIX_DIRECTORY/share/wayland-sessions
# Xorg server command
@@ -224,4 +237,6 @@ xauth_cmd = $PREFIX_DIRECTORY/bin/xauth
xinitrc = ~/.xinitrc
# Xorg desktop environments
# You can specify multiple directories,
# e.g. /usr/share/xsessions:/usr/local/share/xsessions
xsessions = $PREFIX_DIRECTORY/share/xsessions

64
res/lang/ar.ini Normal file
View File

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

View File

@@ -6,9 +6,11 @@ err_alloc = assignació de memòria fallida
err_bounds = índex fora de límits
err_brightness_change = error en canviar la brillantor
err_chdir = error en obrir la carpeta home
err_console_dev = error en accedir a la consola
err_dgn_oob = missatge de registre
err_domain = domini invàlid
err_envlist = error en obtenir l'envlist
err_hostname = error en obtenir el nom de l'amfitrió
err_mlock = error en bloquejar la memòria de clau
@@ -35,6 +37,8 @@ err_perm_dir = error en canviar el directori actual
err_perm_group = error en degradar els permisos de grup
err_perm_user = error en degradar els permisos de l'usuari
err_pwnam = error en obtenir la informació de l'usuari
err_unknown = ha ocorregut un error desconegut
err_user_gid = error en establir el GID de l'usuari
err_user_init = error en inicialitzar usuari
@@ -46,10 +50,10 @@ err_xsessions_open = error en obrir la carpeta de sessions
insert = inserir
login = iniciar sessió
logout = sessió tancada
no_x11_support = x11 support disabled at compile-time
no_x11_support = el suport per x11 ha estat desactivat en la compilació
normal = normal
numlock = Bloq Num
password = Clau
restart = reiniciar
shell = shell

View File

@@ -1,13 +1,21 @@
capslock = capslock
err_alloc = alokace paměti selhala
err_bounds = index je mimo hranice pole
err_chdir = nelze otevřít domovský adresář
err_console_dev = chyba při přístupu do konzole
err_dgn_oob = zpráva protokolu
err_domain = neplatná doména
err_hostname = nelze získat název hostitele
err_mlock = uzamčení paměti hesel selhalo
err_null = nulový ukazatel
err_pam = pam transakce selhala
err_pam_abort = pam transakce přerušena
err_pam_acct_expired = platnost účtu vypršela
@@ -29,17 +37,28 @@ err_perm_dir = nepodařilo se změnit adresář
err_perm_group = nepodařilo se snížit skupinová oprávnění
err_perm_user = nepodařilo se snížit uživatelská oprávnění
err_pwnam = nelze získat informace o uživateli
err_user_gid = nastavení GID uživatele selhalo
err_user_init = inicializace uživatele selhala
err_user_uid = nastavení UID uživateli selhalo
err_xsessions_dir = nepodařilo se najít složku relací
err_xsessions_open = nepodařilo se otevřít složku relací
login = uživatel
logout = odhlášen
numlock = numlock
password = heslo
restart = restartovat
shell = příkazový řádek
shutdown = vypnout
wayland = wayland
xinitrc = xinitrc

View File

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

View File

@@ -6,9 +6,11 @@ err_alloc = failed memory allocation
err_bounds = out-of-bounds index
err_brightness_change = failed to change brightness
err_chdir = failed to open home folder
err_config = unable to parse config file
err_console_dev = failed to access console
err_dgn_oob = log message
err_domain = invalid domain
err_empty_password = empty password not allowed
err_envlist = failed to get envlist
err_hostname = failed to get hostname
err_mlock = failed to lock password memory
@@ -36,6 +38,7 @@ err_perm_group = failed to downgrade group permissions
err_perm_user = failed to downgrade user permissions
err_pwnam = failed to get user info
err_sleep = failed to execute sleep command
err_tty_ctrl = tty control transfer failed
err_unknown = an unknown error occurred
err_user_gid = failed to set user GID
err_user_init = failed to initialize user
@@ -47,14 +50,15 @@ err_xsessions_open = failed to open sessions folder
insert = insert
login = login
logout = logged out
normal = normal
no_x11_support = x11 support disabled at compile-time
normal = normal
numlock = numlock
other = other
password = password
restart = reboot
shell = shell
shutdown = shutdown
sleep = sleep
wayland = wayland
xinitrc = xinitrc
x11 = x11
xinitrc = xinitrc

View File

@@ -4,13 +4,18 @@ brightness_up = subir brillo
capslock = Bloq Mayús
err_alloc = asignación de memoria fallida
err_bounds = índice fuera de límites
err_chdir = error al abrir la carpeta home
err_console_dev = error al acceder a la consola
err_dgn_oob = mensaje de registro
err_domain = dominio inválido
err_hostname = error al obtener el nombre de host
err_mlock = error al bloquear la contraseña de memoria
err_null = puntero nulo
err_pam = error en la transacción pam
err_pam_abort = transacción pam abortada
err_pam_acct_expired = cuenta expirada
@@ -32,9 +37,14 @@ err_perm_dir = error al cambiar el directorio actual
err_perm_group = error al degradar los permisos del grupo
err_perm_user = error al degradar los permisos del usuario
err_pwnam = error al obtener la información del usuario
err_user_gid = error al establecer el GID del usuario
err_user_init = error al inicializar usuario
err_user_uid = error al establecer el UID del usuario
err_xsessions_dir = error al buscar la carpeta de sesiones
err_xsessions_open = error al abrir la carpeta de sesiones
insert = insertar
@@ -50,4 +60,5 @@ shell = shell
shutdown = apagar
sleep = suspender
wayland = wayland
xinitrc = xinitrc

View File

@@ -1,4 +1,4 @@
authenticating = authentification...
authenticating = authentification...
brightness_down = diminuer la luminosité
brightness_up = augmenter la luminosité
capslock = verr.maj
@@ -6,9 +6,11 @@ err_alloc = échec d'allocation mémoire
err_bounds = indice hors-limite
err_brightness_change = échec du changement de luminosité
err_chdir = échec de l'ouverture du répertoire home
err_config = échec de lecture du fichier de configuration
err_console_dev = échec d'accès à la console
err_dgn_oob = message
err_domain = domaine invalide
err_empty_password = mot de passe vide non autorisé
err_envlist = échec de lecture de la liste d'environnement
err_hostname = échec de lecture du nom d'hôte
err_mlock = échec du verrouillage mémoire
@@ -35,6 +37,8 @@ err_perm_dir = échec de changement de répertoire
err_perm_group = échec du déclassement des permissions de groupe
err_perm_user = échec du déclassement des permissions utilisateur
err_pwnam = échec de lecture des infos utilisateur
err_sleep = échec de l'exécution de la commande de veille
err_tty_ctrl = échec du transfert de contrôle du terminal
err_unknown = une erreur inconnue est survenue
err_user_gid = échec de modification du GID
err_user_init = échec d'initialisation de l'utilisateur
@@ -46,14 +50,15 @@ err_xsessions_open = échec de l'ouverture du dossier de sessions
insert = insertion
login = identifiant
logout = déconnecté
normal = normal
no_x11_support = support pour x11 désactivé lors de la compilation
normal = normal
numlock = verr.num
other = autre
password = mot de passe
restart = redémarrer
shell = shell
shutdown = éteindre
sleep = veille
wayland = wayland
xinitrc = xinitrc
x11 = x11
xinitrc = xinitrc

View File

@@ -1,13 +1,21 @@
capslock = capslock
err_alloc = impossibile allocare memoria
err_bounds = indice fuori limite
err_chdir = impossibile aprire home directory
err_console_dev = impossibile aprire console
err_dgn_oob = messaggio log
err_domain = dominio non valido
err_hostname = impossibile ottenere hostname
err_mlock = impossibile ottenere lock per la password in memoria
err_null = puntatore nullo
err_pam = transazione PAM fallita
err_pam_abort = transazione PAM interrotta
err_pam_acct_expired = account scaduto
@@ -29,17 +37,28 @@ err_perm_dir = impossibile cambiare directory corrente
err_perm_group = impossibile ridurre permessi gruppo
err_perm_user = impossibile ridurre permessi utente
err_pwnam = impossibile ottenere dati utente
err_user_gid = impossibile impostare GID utente
err_user_init = impossibile inizializzare utente
err_user_uid = impossible impostare UID utente
err_xsessions_dir = impossibile localizzare cartella sessioni
err_xsessions_open = impossibile aprire cartella sessioni
login = username
logout = scollegato
numlock = numlock
password = password
restart = riavvio
shell = shell
shutdown = arresto
wayland = wayland
xinitrc = xinitrc

View File

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

View File

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

View File

@@ -1,13 +1,21 @@
capslock = capslock
err_alloc = erro na atribuição de memória
err_bounds = índice fora de limites
err_chdir = erro ao abrir a pasta home
err_console_dev = erro ao aceder à consola
err_dgn_oob = mensagem de registo
err_domain = domínio inválido
err_hostname = erro ao obter o nome do host
err_mlock = erro de bloqueio de memória
err_null = ponteiro nulo
err_pam = erro na transação pam
err_pam_abort = transação pam abortada
err_pam_acct_expired = conta expirada
@@ -29,17 +37,28 @@ err_perm_dir = erro ao alterar o diretório atual
err_perm_group = erro ao reduzir as permissões do grupo
err_perm_user = erro ao reduzir as permissões do utilizador
err_pwnam = erro ao obter informação do utilizador
err_user_gid = erro ao definir o GID do utilizador
err_user_init = erro ao iniciar o utilizador
err_user_uid = erro ao definir o UID do utilizador
err_xsessions_dir = erro ao localizar a pasta das sessões
err_xsessions_open = erro ao abrir a pasta das sessões
login = iniciar sessão
logout = terminar sessão
numlock = numlock
password = palavra-passe
restart = reiniciar
shell = shell
shutdown = encerrar
wayland = wayland
xinitrc = xinitrc

View File

@@ -1,13 +1,21 @@
capslock = caixa alta
err_alloc = alocação de memória malsucedida
err_bounds = índice fora de limites
err_chdir = não foi possível abrir o diretório home
err_console_dev = não foi possível acessar o console
err_dgn_oob = mensagem de log
err_domain = domínio inválido
err_hostname = não foi possível obter o nome do host
err_mlock = bloqueio da memória de senha malsucedido
err_null = ponteiro nulo
err_pam = transação pam malsucedida
err_pam_abort = transação pam abortada
err_pam_acct_expired = conta expirada
@@ -29,17 +37,28 @@ err_perm_dir = não foi possível alterar o diretório atual
err_perm_group = não foi possível reduzir as permissões de grupo
err_perm_user = não foi possível reduzir as permissões de usuário
err_pwnam = não foi possível obter informações do usuário
err_user_gid = não foi possível definir o GID do usuário
err_user_init = não foi possível iniciar o usuário
err_user_uid = não foi possível definir o UID do usuário
err_xsessions_dir = não foi possível encontrar a pasta das sessões
err_xsessions_open = não foi possível abrir a pasta das sessões
login = conectar
logout = desconectado
numlock = numlock
password = senha
restart = reiniciar
shell = shell
shutdown = desligar
wayland = wayland
xinitrc = xinitrc

View File

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

View File

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

View File

@@ -1,13 +1,21 @@
capslock = capslock
err_alloc = neuspijesna alokacija memorije
err_bounds = izvan granica indeksa
err_chdir = neuspijesno otvaranje home foldera
err_console_dev = neuspijesno pristupanje konzoli
err_dgn_oob = log poruka
err_domain = nevazeci domen
err_hostname = neuspijesno trazenje hostname-a
err_mlock = neuspijesno zakljucavanje memorije lozinke
err_null = null pokazivac
err_pam = pam transakcija neuspijesna
err_pam_abort = pam transakcija prekinuta
err_pam_acct_expired = nalog istekao
@@ -25,21 +33,32 @@ err_pam_session = greska sesije
err_pam_sys = greska sistema
err_pam_user_unknown = nepoznat korisnik
err_path = neuspjelo postavljanje path-a
err_perm_dir = neuspjelo mijenjanje foldera
err_perm_dir = neuspjelo mijenjanje foldera
err_perm_group = neuspjesno snizavanje dozvola grupe
err_perm_user = neuspijesno snizavanje dozvola korisnika
err_pwnam = neuspijesno skupljanje informacija o korisniku
err_user_gid = neuspijesno postavljanje korisničkog GID-a
err_user_init = neuspijensa inicijalizacija korisnika
err_user_uid = neuspijesno postavljanje UID-a korisnika
err_xsessions_dir = neuspijesno pronalazenje foldera sesija
err_xsessions_open = neuspijesno otvaranje foldera sesija
err_xsessions_open = neuspijesno otvaranje foldera sesija
login = korisnik
logout = izlogovan
numlock = numlock
password = lozinka
restart = ponovo pokreni
shell = shell
shutdown = ugasi
wayland = wayland
xinitrc = xinitrc

View File

@@ -1,13 +1,21 @@
capslock = capslock
err_alloc = misslyckad minnesallokering
err_bounds = utanför banan index
err_chdir = misslyckades att öppna hemkatalog
err_console_dev = misslyckades att komma åt konsol
err_dgn_oob = loggmeddelande
err_domain = okänd domän
err_hostname = misslyckades att hämta värdnamn
err_mlock = misslyckades att låsa lösenordsminne
err_null = nullpekare
err_pam = pam-transaktion misslyckades
err_pam_abort = pam-transaktion avbröts
err_pam_acct_expired = konto upphört
@@ -29,17 +37,28 @@ err_perm_dir = misslyckades att ändra aktuell katalog
err_perm_group = misslyckades att nergradera gruppbehörigheter
err_perm_user = misslyckades att nergradera användarbehörigheter
err_pwnam = misslyckades att hämta användarinfo
err_user_gid = misslyckades att ställa in användar-GID
err_user_init = misslyckades att initialisera användaren
err_user_uid = misslyckades att ställa in användar-UID
err_xsessions_dir = misslyckades att hitta sessionskatalog
err_xsessions_open = misslyckades att öppna sessionskatalog
login = inloggning
logout = utloggad
numlock = numlock
password = lösenord
restart = starta om
shell = skal
shutdown = stäng av
wayland = wayland
xinitrc = xinitrc

View File

@@ -1,13 +1,21 @@
capslock = capslock
err_alloc = basarisiz bellek ayirma
err_bounds = sinirlarin disinda dizin
err_chdir = ev klasoru acilamadi
err_console_dev = konsola erisilemedi
err_dgn_oob = log mesaji
err_domain = gecersiz etki alani
err_hostname = ana bilgisayar adi alinamadi
err_mlock = parola bellegi kilitlenemedi
err_null = bos isaretci hatasi
err_pam = pam islemi basarisiz oldu
err_pam_abort = pam islemi durduruldu
err_pam_acct_expired = hesabin suresi dolmus
@@ -29,17 +37,28 @@ err_perm_dir = gecerli dizin degistirilemedi
err_perm_group = grup izinleri dusurulemedi
err_perm_user = kullanici izinleri dusurulemedi
err_pwnam = kullanici bilgileri alinamadi
err_user_gid = kullanici icin GID ayarlanamadi
err_user_init = kullanici oturumu baslatilamadi
err_user_uid = kullanici icin UID ayarlanamadi
err_xsessions_dir = oturumlar klasoru bulunamadi
err_xsessions_open = oturumlar klasoru acilamadi
login = kullanici
logout = oturumdan cikis yapildi
numlock = numlock
password = sifre
restart = yeniden baslat
shell = shell
shutdown = makineyi kapat
wayland = wayland
xinitrc = xinitrc

View File

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

64
res/lang/zh_CN.ini Normal file
View File

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

View File

@@ -89,6 +89,10 @@ if [ "$XDG_SESSION_TYPE" = "x11" ]; then
done
fi
if [ -f "$USERXSESSION" ]; then
. "$USERXSESSION"
fi
if [ -d "$CONFIG_DIRECTORY"/X11/Xresources ]; then
for i in "$CONFIG_DIRECTORY"/X11/Xresources/*; do
[ -f "$i" ] && xrdb -merge "$i"
@@ -98,10 +102,6 @@ if [ "$XDG_SESSION_TYPE" = "x11" ]; then
fi
[ -f "$HOME"/.Xresources ] && xrdb -merge "$HOME"/.Xresources
[ -f "$XDG_CONFIG_HOME"/X11/Xresources ] && xrdb -merge "$XDG_CONFIG_HOME"/X11/Xresources
if [ -f "$USERXSESSION" ]; then
. "$USERXSESSION"
fi
fi
exec "$@"

View File

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

21
src/Environment.zig Normal file
View File

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

View File

@@ -9,7 +9,7 @@ const ErrorHandler = packed struct {
const SharedError = @This();
data: []align(std.mem.page_size) u8,
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);

View File

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

View File

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

14
src/animations/Dummy.zig Normal file
View File

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

View File

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

View File

@@ -2,16 +2,26 @@ 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 TerminalBuffer = @import("tui/TerminalBuffer.zig");
const Session = @import("tui/components/Session.zig");
const Text = @import("tui/components/Text.zig");
const Config = @import("config/Config.zig");
const SharedError = @import("SharedError.zig");
const Allocator = std.mem.Allocator;
const Md5 = std.crypto.hash.Md5;
const utmp = interop.utmp;
const Utmp = utmp.utmpx;
const SharedError = @import("SharedError.zig");
pub const AuthOptions = struct {
tty: u8,
service_name: [:0]const u8,
path: ?[:0]const u8,
session_log: []const u8,
xauth_cmd: []const u8,
setup_cmd: []const u8,
login_cmd: ?[]const u8,
x_cmd: []const u8,
session_pid: std.posix.pid_t,
};
var xorg_pid: std.posix.pid_t = 0;
pub fn xorgSignalHandler(i: c_int) callconv(.C) void {
@@ -23,16 +33,16 @@ pub fn sessionSignalHandler(i: c_int) callconv(.C) void {
if (child_pid > 0) _ = std.c.kill(child_pid, i);
}
pub fn authenticate(config: Config, current_environment: Session.Environment, login: [:0]const u8, password: [:0]const u8) !void {
pub fn authenticate(options: AuthOptions, current_environment: Environment, login: [:0]const u8, password: [:0]const u8) !void {
var tty_buffer: [3]u8 = undefined;
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty});
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{options.tty});
var pam_tty_buffer: [6]u8 = undefined;
const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{config.tty});
const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{options.tty});
// Set the XDG environment variables
setXdgSessionEnv(current_environment.display_server);
try setXdgEnv(tty_str, current_environment.xdg_session_desktop orelse "", current_environment.xdg_desktop_names orelse "");
try setXdgEnv(tty_str, current_environment.xdg_session_desktop, current_environment.xdg_desktop_names);
// Open the PAM session
var credentials = [_:null]?[*:0]const u8{ login, password };
@@ -43,7 +53,7 @@ pub fn authenticate(config: Config, current_environment: Session.Environment, lo
};
var handle: ?*interop.pam.pam_handle = undefined;
var status = interop.pam.pam_start(config.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);
defer _ = interop.pam.pam_end(handle, status);
@@ -86,7 +96,7 @@ pub fn authenticate(config: Config, current_environment: Session.Environment, lo
child_pid = try std.posix.fork();
if (child_pid == 0) {
startSession(config, pwd, handle, current_environment) catch |e| {
startSession(options, pwd, handle, current_environment) catch |e| {
shared_err.writeError(e);
std.process.exit(1);
};
@@ -108,7 +118,7 @@ pub fn authenticate(config: Config, current_environment: Session.Environment, lo
.mask = std.posix.empty_sigset,
.flags = 0,
};
try std.posix.sigaction(std.posix.SIG.TERM, &act, null);
std.posix.sigaction(std.posix.SIG.TERM, &act, null);
try addUtmpEntry(&entry, pwd.pw_name.?, child_pid);
}
@@ -121,10 +131,10 @@ pub fn authenticate(config: Config, current_environment: Session.Environment, lo
}
fn startSession(
config: Config,
options: AuthOptions,
pwd: *interop.pwd.passwd,
handle: ?*interop.pam.pam_handle,
current_environment: Session.Environment,
current_environment: Environment,
) !void {
if (builtin.os.tag == .freebsd) {
// FreeBSD has initgroups() in unistd
@@ -143,7 +153,7 @@ fn startSession(
}
// Set up the environment
try initEnv(pwd, config.path);
try initEnv(pwd, options.path);
// Set the PAM variables
const pam_env_vars: ?[*:null]?[*:0]u8 = interop.pam.pam_getenvlist(handle);
@@ -155,14 +165,17 @@ fn startSession(
// Change to the user's home directory
std.posix.chdirZ(pwd.pw_dir.?) catch return error.ChangeDirectoryFailed;
// Signal to the session process to give up control on the TTY
_ = std.posix.kill(options.session_pid, std.posix.SIG.CHLD) catch return error.TtyControlTransferFailed;
// Execute what the user requested
switch (current_environment.display_server) {
.wayland => try executeWaylandCmd(pwd.pw_shell.?, config, current_environment.cmd),
.shell => try executeShellCmd(pwd.pw_shell.?, config),
.wayland => try executeWaylandCmd(pwd.pw_shell.?, options, current_environment.cmd),
.shell => try executeShellCmd(pwd.pw_shell.?, options),
.xinitrc, .x11 => if (build_options.enable_x11_support) {
var vt_buf: [5]u8 = undefined;
const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{config.tty});
try executeX11Cmd(pwd.pw_shell.?, pwd.pw_dir.?, config, current_environment.cmd, vt);
const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{options.tty});
try executeX11Cmd(pwd.pw_shell.?, pwd.pw_dir.?, options, current_environment.cmd, vt);
},
}
}
@@ -188,7 +201,7 @@ fn setXdgSessionEnv(display_server: enums.DisplayServer) void {
}, 0);
}
fn setXdgEnv(tty_str: [:0]u8, desktop_name: [:0]const u8, xdg_desktop_names: [:0]const u8) !void {
fn setXdgEnv(tty_str: [:0]u8, maybe_desktop_name: ?[:0]const u8, maybe_xdg_desktop_names: ?[:0]const u8) !void {
// The "/run/user/%d" directory is not available on FreeBSD. It is much
// better to stick to the defaults and let applications using
// XDG_RUNTIME_DIR to fall back to directories inside user's home
@@ -201,10 +214,10 @@ fn setXdgEnv(tty_str: [:0]u8, desktop_name: [:0]const u8, xdg_desktop_names: [:0
_ = interop.stdlib.setenv("XDG_RUNTIME_DIR", uid_str, 0);
}
_ = interop.stdlib.setenv("XDG_CURRENT_DESKTOP", xdg_desktop_names, 0);
if (maybe_xdg_desktop_names) |xdg_desktop_names| _ = interop.stdlib.setenv("XDG_CURRENT_DESKTOP", xdg_desktop_names, 0);
_ = interop.stdlib.setenv("XDG_SESSION_CLASS", "user", 0);
_ = interop.stdlib.setenv("XDG_SESSION_ID", "1", 0);
_ = interop.stdlib.setenv("XDG_SESSION_DESKTOP", desktop_name, 0);
if (maybe_desktop_name) |desktop_name| _ = interop.stdlib.setenv("XDG_SESSION_DESKTOP", desktop_name, 0);
_ = interop.stdlib.setenv("XDG_SEAT", "seat0", 0);
_ = interop.stdlib.setenv("XDG_VTNR", tty_str, 0);
}
@@ -350,7 +363,7 @@ fn mcookie() [Md5.digest_length * 2]u8 {
return std.fmt.bytesToHex(&out, .lower);
}
fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, config: Config) !void {
fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptions) !void {
var pwd_buf: [100]u8 = undefined;
const pwd = try std.fmt.bufPrintZ(&pwd_buf, "{s}", .{pw_dir});
@@ -362,11 +375,8 @@ fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, conf
const pid = try std.posix.fork();
if (pid == 0) {
const log_file = try redirectStandardStreams(config.session_log, true);
defer log_file.close();
var cmd_buffer: [1024]u8 = undefined;
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . {s}", .{ config.xauth_cmd, display_name, magic_cookie }) catch std.process.exit(1);
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . {s}", .{ options.xauth_cmd, display_name, magic_cookie }) catch std.process.exit(1);
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
std.posix.execveZ(shell, &args, std.c.environ) catch {};
std.process.exit(1);
@@ -376,35 +386,35 @@ fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, conf
if (status.status != 0) return error.XauthFailed;
}
fn executeShellCmd(shell: [*:0]const u8, config: Config) !void {
fn executeShellCmd(shell: [*:0]const u8, options: AuthOptions) !void {
// We don't want to redirect stdout and stderr in a shell session
var cmd_buffer: [1024]u8 = undefined;
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ config.setup_cmd, config.login_cmd orelse "", shell });
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", shell });
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
return std.posix.execveZ(shell, &args, std.c.environ);
}
fn executeWaylandCmd(shell: [*:0]const u8, config: Config, desktop_cmd: []const u8) !void {
const log_file = try redirectStandardStreams(config.session_log, true);
fn executeWaylandCmd(shell: [*:0]const u8, options: AuthOptions, desktop_cmd: []const u8) !void {
const log_file = try redirectStandardStreams(options.session_log, true);
defer log_file.close();
var cmd_buffer: [1024]u8 = undefined;
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ config.setup_cmd, config.login_cmd orelse "", desktop_cmd });
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", desktop_cmd });
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
return std.posix.execveZ(shell, &args, std.c.environ);
}
fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, config: Config, desktop_cmd: []const u8, vt: []const u8) !void {
fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptions, desktop_cmd: []const u8, vt: []const u8) !void {
const display_num = try getFreeDisplay();
var buf: [5]u8 = undefined;
const display_name = try std.fmt.bufPrintZ(&buf, ":{d}", .{display_num});
try xauth(display_name, shell, pw_dir, config);
try xauth(display_name, shell, pw_dir, options);
const pid = try std.posix.fork();
if (pid == 0) {
var cmd_buffer: [1024]u8 = undefined;
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s} >{s} 2>&1", .{ config.x_cmd, display_name, vt, config.session_log }) catch std.process.exit(1);
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.x_cmd, display_name, vt }) catch std.process.exit(1);
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
std.posix.execveZ(shell, &args, std.c.environ) catch {};
std.process.exit(1);
@@ -427,7 +437,7 @@ fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, config: Config, de
xorg_pid = try std.posix.fork();
if (xorg_pid == 0) {
var cmd_buffer: [1024]u8 = undefined;
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s} >{s} 2>&1", .{ config.setup_cmd, config.login_cmd orelse "", desktop_cmd, config.session_log }) catch std.process.exit(1);
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", desktop_cmd }) catch std.process.exit(1);
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
std.posix.execveZ(shell, &args, std.c.environ) catch {};
std.process.exit(1);
@@ -439,13 +449,13 @@ fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, config: Config, de
.mask = std.posix.empty_sigset,
.flags = 0,
};
try std.posix.sigaction(std.posix.SIG.TERM, &act, null);
std.posix.sigaction(std.posix.SIG.TERM, &act, null);
_ = std.posix.waitpid(xorg_pid, 0);
interop.xcb.xcb_disconnect(xcb);
std.posix.kill(x_pid, 0) catch return;
std.posix.kill(x_pid, std.posix.SIG.TERM) catch {};
std.posix.kill(x_pid, std.posix.SIG.KILL) catch {};
var status: c_int = 0;
_ = std.c.waitpid(x_pid, &status, 0);

View File

@@ -1,45 +1,44 @@
const std = @import("std");
const interop = @import("interop.zig");
const utils = @import("tui/utils.zig");
const enums = @import("enums.zig");
const Lang = @import("bigclock/Lang.zig");
const en = @import("bigclock/en.zig");
const fa = @import("bigclock/fa.zig");
const Lang = @import("bigclock/Lang.zig");
const en = @import("bigclock/en.zig");
const fa = @import("bigclock/fa.zig");
const Cell = @import("tui/Cell.zig");
const termbox = interop.termbox;
const Bigclock = enums.Bigclock;
pub const WIDTH = Lang.WIDTH;
const Bigclock = enums.Bigclock;
pub const WIDTH = Lang.WIDTH;
pub const HEIGHT = Lang.HEIGHT;
pub const SIZE = Lang.SIZE;
pub const SIZE = Lang.SIZE;
pub fn clockCell(animate: bool, char: u8, fg: u16, bg: u16, bigclock: Bigclock) [SIZE]utils.Cell {
var cells: [SIZE]utils.Cell = undefined;
pub fn clockCell(animate: bool, char: u8, fg: u32, bg: u32, bigclock: Bigclock) [SIZE]Cell {
var cells: [SIZE]Cell = undefined;
var tv: interop.system_time.timeval = undefined;
_ = interop.system_time.gettimeofday(&tv, null);
const clock_chars = toBigNumber(if (animate and char == ':' and @divTrunc(tv.tv_usec, 500000) != 0) ' ' else char, bigclock);
for (0..cells.len) |i| cells[i] = utils.initCell(clock_chars[i], fg, bg);
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]utils.Cell) void {
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];
if (cell.ch != 0) utils.putCell(x + xx, y + yy, cell);
cell.put(x + xx, y + yy);
}
}
}
fn toBigNumber(char: u8, bigclock: Bigclock) []const u21 {
const locale_chars = switch (bigclock) {
.fa => fa.locale_chars,
.en => en.locale_chars,
.none => unreachable,
.fa => fa.locale_chars,
.en => en.locale_chars,
.none => unreachable,
};
return switch (char) {
'0' => &locale_chars.ZERO,

View File

@@ -6,28 +6,36 @@ const Input = enums.Input;
const ViMode = enums.ViMode;
const Bigclock = enums.Bigclock;
allow_empty_password: bool = true,
animation: Animation = .none,
animation_timeout_sec: u12 = 0,
animation_refresh_ms: u16 = 16,
asterisk: ?u8 = '*',
asterisk: ?u32 = '*',
auth_fails: u64 = 10,
bg: u16 = 0,
bg: u32 = 0x00000000,
bigclock: Bigclock = .none,
blank_box: bool = true,
border_fg: u16 = 8,
border_fg: u32 = 0x00FFFFFF,
box_title: ?[]const u8 = null,
brightness_down_cmd: [:0]const u8 = build_options.prefix_directory ++ "/bin/brightnessctl -q s 10%-",
brightness_down_key: []const u8 = "F5",
brightness_down_key: ?[]const u8 = "F5",
brightness_up_cmd: [:0]const u8 = build_options.prefix_directory ++ "/bin/brightnessctl -q s +10%",
brightness_up_key: []const u8 = "F6",
brightness_up_key: ?[]const u8 = "F6",
clear_password: bool = false,
clock: ?[:0]const u8 = null,
cmatrix_fg: u16 = 3,
cmatrix_fg: u32 = 0x0000FF00,
cmatrix_min_codepoint: u16 = 0x21,
cmatrix_max_codepoint: u16 = 0x7B,
colormix_col1: u32 = 0x00FF0000,
colormix_col2: u32 = 0x000000FF,
colormix_col3: u32 = 0x20000000,
console_dev: []const u8 = "/dev/console",
default_input: Input = .login,
error_bg: u16 = 0,
error_fg: u16 = 258,
fg: u16 = 8,
doom_top_color: u32 = 0x00FF0000,
doom_middle_color: u32 = 0x00FFFF00,
doom_bottom_color: u32 = 0x00FFFFFF,
error_bg: u32 = 0x00000000,
error_fg: u32 = 0x01FF0000,
fg: u32 = 0x00FFFFFF,
hide_borders: bool = false,
hide_key_hints: bool = false,
initial_info_text: ?[]const u8 = null,

View File

@@ -1,3 +1,8 @@
//
// NOTE: After editing this file, please run `/res/lang/normalize_lang_files.py`
// to update all the language files accordingly.
//
authenticating: []const u8 = "authenticating...",
brightness_down: []const u8 = "decrease brightness",
brightness_up: []const u8 = "increase brightness",
@@ -10,6 +15,7 @@ err_config: []const u8 = "unable to parse config file",
err_console_dev: []const u8 = "failed to access console",
err_dgn_oob: []const u8 = "log message",
err_domain: []const u8 = "invalid domain",
err_empty_password: []const u8 = "empty password not allowed",
err_envlist: []const u8 = "failed to get envlist",
err_hostname: []const u8 = "failed to get hostname",
err_mlock: []const u8 = "failed to lock password memory",
@@ -37,6 +43,7 @@ err_perm_group: []const u8 = "failed to downgrade group permissions",
err_perm_user: []const u8 = "failed to downgrade user permissions",
err_pwnam: []const u8 = "failed to get user info",
err_sleep: []const u8 = "failed to execute sleep command",
err_tty_ctrl: []const u8 = "tty control transfer failed",
err_unknown: []const u8 = "an unknown error occurred",
err_user_gid: []const u8 = "failed to set user GID",
err_user_init: []const u8 = "failed to initialize user",
@@ -48,8 +55,8 @@ err_xsessions_open: []const u8 = "failed to open sessions folder",
insert: []const u8 = "insert",
login: []const u8 = "login:",
logout: []const u8 = "logged out",
normal: []const u8 = "normal",
no_x11_support: []const u8 = "x11 support disabled at compile-time",
normal: []const u8 = "normal",
numlock: []const u8 = "numlock",
other: []const u8 = "other",
password: []const u8 = "password:",
@@ -58,5 +65,5 @@ shell: [:0]const u8 = "shell",
shutdown: []const u8 = "shutdown",
sleep: []const u8 = "sleep",
wayland: []const u8 = "wayland",
xinitrc: [:0]const u8 = "xinitrc",
x11: []const u8 = "x11",
xinitrc: [:0]const u8 = "xinitrc",

View File

@@ -5,7 +5,31 @@ const ini = @import("zigini");
const Save = @import("Save.zig");
const enums = @import("../enums.zig");
const color_properties = [_][]const u8{
"bg",
"border_fg",
"cmatrix_fg",
"colormix_col1",
"colormix_col2",
"colormix_col3",
"error_bg",
"error_fg",
"fg",
};
const removed_properties = [_][]const u8{
"wayland_specifier",
"max_desktop_len",
"max_login_len",
"max_password_len",
"mcookie_cmd",
"term_reset_cmd",
"term_restore_cursor_cmd",
"x_cmd_setup",
"wayland_cmd",
};
var temporary_allocator = std.heap.page_allocator;
var buffer = std.mem.zeroes([10 * color_properties.len]u8);
pub var maybe_animate: ?bool = null;
pub var maybe_save_file: ?[]const u8 = null;
@@ -37,6 +61,18 @@ pub fn configFieldHandler(_: std.mem.Allocator, field: ini.IniField) ?ini.IniFie
return mapped_field;
}
inline for (color_properties) |property| {
if (std.mem.eql(u8, field.key, property)) {
// These options now uses a 32-bit RGB value instead of an arbitrary 16-bit integer
const color = std.fmt.parseInt(u16, field.value, 0) catch return field;
var mapped_field = field;
mapped_field.value = mapColor(color) catch return field;
mapped_config_fields = true;
return mapped_field;
}
}
if (std.mem.eql(u8, field.key, "blank_password")) {
// The option has simply been renamed
var mapped_field = field;
@@ -70,19 +106,12 @@ pub fn configFieldHandler(_: std.mem.Allocator, field: ini.IniField) ?ini.IniFie
return null;
}
if (std.mem.eql(u8, field.key, "wayland_specifier") or
std.mem.eql(u8, field.key, "max_desktop_len") or
std.mem.eql(u8, field.key, "max_login_len") or
std.mem.eql(u8, field.key, "max_password_len") or
std.mem.eql(u8, field.key, "mcookie_cmd") or
std.mem.eql(u8, field.key, "term_reset_cmd") or
std.mem.eql(u8, field.key, "term_restore_cursor_cmd") or
std.mem.eql(u8, field.key, "x_cmd_setup") or
std.mem.eql(u8, field.key, "wayland_cmd"))
{
// The options don't exist anymore
mapped_config_fields = true;
return null;
inline for (removed_properties) |property| {
if (std.mem.eql(u8, field.key, property)) {
// The options don't exist anymore
mapped_config_fields = true;
return null;
}
}
if (std.mem.eql(u8, field.key, "bigclock")) {
@@ -90,14 +119,14 @@ pub fn configFieldHandler(_: std.mem.Allocator, field: ini.IniField) ?ini.IniFie
// It also includes the ability to change active bigclock's language
var mapped_field = field;
if (std.mem.eql(u8, field.value, "true")){
if (std.mem.eql(u8, field.value, "true")) {
mapped_field.value = "en";
mapped_config_fields = true;
}else if (std.mem.eql(u8, field.value, "false")){
} else if (std.mem.eql(u8, field.value, "false")) {
mapped_field.value = "none";
mapped_config_fields = true;
}
return mapped_field;
}
@@ -142,3 +171,33 @@ pub fn tryMigrateSaveFile(user_buf: *[32]u8) Save {
return save;
}
fn mapColor(color: u16) ![]const u8 {
const color_no_styling = color & 0x00FF;
const styling_only = color & 0xFF00;
// If color is "greater" than TB_WHITE, or the styling is "greater" than TB_DIM,
// we have an invalid color, so return an error
if (color_no_styling > 0x0008 or styling_only > 0x8000) return error.InvalidColor;
var new_color: u32 = switch (color_no_styling) {
0x0000 => 0x00000000, // Default
0x0001 => 0x20000000, // "Hi-black" styling
0x0002 => 0x00FF0000, // Red
0x0003 => 0x0000FF00, // Green
0x0004 => 0x00FFFF00, // Yellow
0x0005 => 0x000000FF, // Blue
0x0006 => 0x00FF00FF, // Magenta
0x0007 => 0x0000FFFF, // Cyan
0x0008 => 0x00FFFFFF, // White
else => unreachable,
};
// Only applying styling if color isn't black and styling isn't also black
if (!(new_color == 0x20000000 and styling_only == 0x20000000)) {
// Shift styling by 16 to the left to apply it to the new 32-bit color
new_color |= @as(u32, @intCast(styling_only)) << 16;
}
return try std.fmt.bufPrint(&buffer, "0x{X}", .{new_color});
}

View File

@@ -2,6 +2,7 @@ pub const Animation = enum {
none,
doom,
matrix,
colormix,
};
pub const DisplayServer = enum {
@@ -27,4 +28,4 @@ pub const Bigclock = enum {
none,
en,
fa,
};
};

View File

@@ -5,9 +5,14 @@ const clap = @import("clap");
const ini = @import("zigini");
const auth = @import("auth.zig");
const bigclock = @import("bigclock.zig");
const enums = @import("enums.zig");
const Environment = @import("Environment.zig");
const interop = @import("interop.zig");
const ColorMix = @import("animations/ColorMix.zig");
const Doom = @import("animations/Doom.zig");
const Dummy = @import("animations/Dummy.zig");
const Matrix = @import("animations/Matrix.zig");
const Animation = @import("tui/Animation.zig");
const TerminalBuffer = @import("tui/TerminalBuffer.zig");
const Session = @import("tui/components/Session.zig");
const Text = @import("tui/components/Text.zig");
@@ -17,15 +22,17 @@ const Lang = @import("config/Lang.zig");
const Save = @import("config/Save.zig");
const migrator = @import("config/migrator.zig");
const SharedError = @import("SharedError.zig");
const utils = @import("tui/utils.zig");
const Ini = ini.Ini;
const DisplayServer = enums.DisplayServer;
const Entry = Environment.Entry;
const termbox = interop.termbox;
const unistd = interop.unistd;
const temporary_allocator = std.heap.page_allocator;
const ly_top_str = "Ly version " ++ build_options.version;
var session_pid: std.posix.pid_t = -1;
pub fn signalHandler(i: c_int) callconv(.C) void {
fn signalHandler(i: c_int) callconv(.C) void {
if (session_pid == 0) return;
// Forward signal to session to clean up
@@ -39,6 +46,10 @@ pub fn signalHandler(i: c_int) callconv(.C) void {
std.c.exit(i);
}
fn ttyControlTransferSignalHandler(_: c_int) callconv(.C) void {
_ = termbox.tb_shutdown();
}
pub fn main() !void {
var shutdown = false;
var restart = false;
@@ -62,11 +73,10 @@ pub fn main() !void {
}
}
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var gpa = std.heap.DebugAllocator(.{}).init;
defer _ = gpa.deinit();
// to be able to stop the animation after some time
// Allows stopping an animation after some time
var tv_zero: interop.system_time.timeval = undefined;
_ = interop.system_time.gettimeofday(&tv_zero, null);
var animation_timed_out: bool = false;
@@ -127,7 +137,10 @@ pub fn main() !void {
const config_path = try std.fmt.allocPrint(allocator, "{s}{s}config.ini", .{ s, trailing_slash });
defer allocator.free(config_path);
config = config_ini.readFileToStruct(config_path, comment_characters, migrator.configFieldHandler) catch _config: {
config = config_ini.readFileToStruct(config_path, .{
.fieldHandler = migrator.configFieldHandler,
.comment_characters = comment_characters,
}) catch _config: {
config_load_failed = true;
break :_config Config{};
};
@@ -135,21 +148,30 @@ pub fn main() !void {
const lang_path = try std.fmt.allocPrint(allocator, "{s}{s}lang/{s}.ini", .{ s, trailing_slash, config.lang });
defer allocator.free(lang_path);
lang = lang_ini.readFileToStruct(lang_path, comment_characters, null) catch Lang{};
lang = lang_ini.readFileToStruct(lang_path, .{
.fieldHandler = null,
.comment_characters = comment_characters,
}) catch Lang{};
if (config.load) {
save_path = try std.fmt.allocPrint(allocator, "{s}{s}save.ini", .{ s, trailing_slash });
save_path_alloc = true;
var user_buf: [32]u8 = undefined;
save = save_ini.readFileToStruct(save_path, comment_characters, null) catch migrator.tryMigrateSaveFile(&user_buf);
save = save_ini.readFileToStruct(save_path, .{
.fieldHandler = null,
.comment_characters = comment_characters,
}) catch migrator.tryMigrateSaveFile(&user_buf);
}
migrator.lateConfigFieldHandler(&config.animation);
} else {
const config_path = build_options.config_directory ++ "/ly/config.ini";
config = config_ini.readFileToStruct(config_path, comment_characters, migrator.configFieldHandler) catch _config: {
config = config_ini.readFileToStruct(config_path, .{
.fieldHandler = migrator.configFieldHandler,
.comment_characters = comment_characters,
}) catch _config: {
config_load_failed = true;
break :_config Config{};
};
@@ -157,11 +179,17 @@ pub fn main() !void {
const lang_path = try std.fmt.allocPrint(allocator, "{s}/ly/lang/{s}.ini", .{ build_options.config_directory, config.lang });
defer allocator.free(lang_path);
lang = lang_ini.readFileToStruct(lang_path, comment_characters, null) catch Lang{};
lang = lang_ini.readFileToStruct(lang_path, .{
.fieldHandler = null,
.comment_characters = comment_characters,
}) catch Lang{};
if (config.load) {
var user_buf: [32]u8 = undefined;
save = save_ini.readFileToStruct(save_path, comment_characters, null) catch migrator.tryMigrateSaveFile(&user_buf);
save = save_ini.readFileToStruct(save_path, .{
.fieldHandler = null,
.comment_characters = comment_characters,
}) catch migrator.tryMigrateSaveFile(&user_buf);
}
migrator.lateConfigFieldHandler(&config.animation);
@@ -191,9 +219,9 @@ pub fn main() !void {
.mask = std.posix.empty_sigset,
.flags = 0,
};
try std.posix.sigaction(std.posix.SIG.TERM, &act, null);
std.posix.sigaction(std.posix.SIG.TERM, &act, null);
_ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_NORMAL);
_ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_TRUECOLOR);
_ = termbox.tb_clear();
// Needed to reset termbox after auth
@@ -208,7 +236,15 @@ pub fn main() !void {
var prng = std.Random.DefaultPrng.init(seed);
const random = prng.random();
var buffer = TerminalBuffer.init(config, labels_max_length, random);
const buffer_options = TerminalBuffer.InitOptions{
.fg = config.fg,
.bg = config.bg,
.border_fg = config.border_fg,
.margin_box_h = config.margin_box_h,
.margin_box_v = config.margin_box_v,
.input_len = config.input_len,
};
var buffer = TerminalBuffer.init(buffer_options, labels_max_length, random);
// Initialize components
var info_line = InfoLine.init(allocator, &buffer);
@@ -223,16 +259,16 @@ pub fn main() !void {
try info_line.addMessage(lang.err_numlock, config.error_bg, config.error_fg);
};
var session = Session.init(allocator, &buffer, lang);
var session = Session.init(allocator, &buffer);
defer session.deinit();
session.addEnvironment(.{ .Name = lang.shell }, null, .shell) catch {
addOtherEnvironment(&session, lang, .shell, null) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
};
if (build_options.enable_x11_support) {
if (config.xinitrc) |xinitrc| {
session.addEnvironment(.{ .Name = lang.xinitrc, .Exec = xinitrc }, null, .xinitrc) catch {
if (config.xinitrc) |xinitrc_cmd| {
addOtherEnvironment(&session, lang, .xinitrc, xinitrc_cmd) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
};
}
@@ -252,8 +288,16 @@ pub fn main() !void {
try info_line.addMessage(hostname, config.bg, config.fg);
}
try session.crawl(config.waylandsessions, .wayland);
if (build_options.enable_x11_support) try session.crawl(config.xsessions, .x11);
var wayland_session_dirs = std.mem.splitScalar(u8, config.waylandsessions, ':');
while (wayland_session_dirs.next()) |dir| {
try crawl(&session, lang, dir, .wayland);
}
if (build_options.enable_x11_support) {
var x_session_dirs = std.mem.splitScalar(u8, config.xsessions, ':');
while (x_session_dirs.next()) |dir| {
try crawl(&session, lang, dir, .x11);
}
}
var login = Text.init(allocator, &buffer, false, null);
defer login.deinit();
@@ -267,7 +311,7 @@ pub fn main() !void {
// Load last saved username and desktop selection, if any
if (config.load) {
if (save.user) |user| {
try login.text.appendSlice(user);
try login.text.appendSlice(login.allocator, user);
login.end = user.len;
login.cursor = login.end;
active_input = .password;
@@ -301,56 +345,59 @@ pub fn main() !void {
}
// Initialize the animation, if any
var doom: Doom = undefined;
var matrix: Matrix = undefined;
var animation: Animation = undefined;
switch (config.animation) {
.none => {},
.doom => doom = try Doom.init(allocator, &buffer),
.matrix => matrix = try Matrix.init(allocator, &buffer, config.cmatrix_fg),
.none => {
var dummy = Dummy{};
animation = dummy.animation();
},
.doom => {
var doom = try Doom.init(allocator, &buffer, config.doom_top_color, config.doom_middle_color, config.doom_bottom_color);
animation = doom.animation();
},
.matrix => {
var matrix = try Matrix.init(allocator, &buffer, config.cmatrix_fg, config.cmatrix_min_codepoint, config.cmatrix_max_codepoint);
animation = matrix.animation();
},
.colormix => {
var color_mix = ColorMix.init(&buffer, config.colormix_col1, config.colormix_col2, config.colormix_col3);
animation = color_mix.animation();
},
}
defer {
switch (config.animation) {
.none => {},
.doom => doom.deinit(),
.matrix => matrix.deinit(),
}
}
var animation_timer = switch (config.animation) {
.none => undefined,
else => try std.time.Timer.start(),
};
defer animation.deinit();
const animate = config.animation != .none;
const shutdown_key = try std.fmt.parseInt(u8, config.shutdown_key[1..], 10);
const shutdown_len = try utils.strWidth(lang.shutdown);
const shutdown_len = try TerminalBuffer.strWidth(lang.shutdown);
const restart_key = try std.fmt.parseInt(u8, config.restart_key[1..], 10);
const restart_len = try utils.strWidth(lang.restart);
const restart_len = try TerminalBuffer.strWidth(lang.restart);
const sleep_key = try std.fmt.parseInt(u8, config.sleep_key[1..], 10);
const sleep_len = try utils.strWidth(lang.sleep);
const brightness_down_key = try std.fmt.parseInt(u8, config.brightness_down_key[1..], 10);
const brightness_down_len = try utils.strWidth(lang.brightness_down);
const brightness_up_key = try std.fmt.parseInt(u8, config.brightness_up_key[1..], 10);
const brightness_up_len = try utils.strWidth(lang.brightness_up);
const sleep_len = try TerminalBuffer.strWidth(lang.sleep);
const brightness_down_key = if (config.brightness_down_key) |key| try std.fmt.parseInt(u8, key[1..], 10) else null;
const brightness_down_len = try TerminalBuffer.strWidth(lang.brightness_down);
const brightness_up_key = if (config.brightness_up_key) |key| try std.fmt.parseInt(u8, key[1..], 10) else null;
const brightness_up_len = try TerminalBuffer.strWidth(lang.brightness_up);
var event: termbox.tb_event = undefined;
var run = true;
var update = true;
var resolution_changed = false;
var auth_fails: u64 = 0;
var can_access_console_dev = true;
// Switch to selected TTY if possible
interop.switchTty(config.console_dev, config.tty) catch {
try info_line.addMessage(lang.err_console_dev, config.error_bg, config.error_fg);
can_access_console_dev = false;
};
while (run) {
// If there's no input or there's an animation, a resolution change needs to be checked
if (!update or config.animation != .none) {
if (!update) std.time.sleep(std.time.ns_per_ms * 100);
if (!update or animate) {
if (!update) std.Thread.sleep(std.time.ns_per_ms * 100);
_ = termbox.tb_present(); // Required to update tb_width(), tb_height() and tb_cell_buffer()
_ = termbox.tb_present(); // Required to update tb_width() and tb_height()
const width: usize = @intCast(termbox.tb_width());
const height: usize = @intCast(termbox.tb_height());
@@ -360,17 +407,10 @@ pub fn main() !void {
buffer.width = width;
buffer.height = height;
buffer.buffer = termbox.tb_cell_buffer();
switch (config.animation) {
.none => {},
.doom => doom.realloc() catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
},
.matrix => matrix.realloc() catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
},
}
animation.realloc() catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
};
update = true;
resolution_changed = true;
@@ -382,27 +422,9 @@ pub fn main() !void {
if (auth_fails < config.auth_fails) {
_ = termbox.tb_clear();
if (!animation_timed_out) {
switch (config.animation) {
.none => {},
.doom => {
if (animation_timer.read() / std.time.ns_per_ms > config.animation_refresh_ms) {
animation_timer.reset();
doom.drawWithUpdate();
} else {
doom.draw();
}
},
.matrix => {
if (animation_timer.read() / std.time.ns_per_ms > config.animation_refresh_ms) {
animation_timer.reset();
matrix.drawWithUpdate();
} else {
matrix.draw();
}
},
}
}
if (!animation_timed_out) animation.draw();
buffer.drawLabel(ly_top_str, 0, 0);
if (config.bigclock != .none and buffer.box_height + (bigclock.HEIGHT + 2) * 2 < buffer.height) draw_big_clock: {
const format = "%H:%M";
@@ -463,7 +485,7 @@ pub fn main() !void {
info_line.label.draw();
if (!config.hide_key_hints) {
var length: usize = 0;
var length: usize = ly_top_str.len + 1;
buffer.drawLabel(config.shutdown_key, length, 0);
length += config.shutdown_key.len + 1;
@@ -488,19 +510,23 @@ pub fn main() !void {
length += sleep_len + 1;
}
buffer.drawLabel(config.brightness_down_key, length, 0);
length += config.brightness_down_key.len + 1;
buffer.drawLabel(" ", length - 1, 0);
if (config.brightness_down_key) |key| {
buffer.drawLabel(key, length, 0);
length += key.len + 1;
buffer.drawLabel(" ", length - 1, 0);
buffer.drawLabel(lang.brightness_down, length, 0);
length += brightness_down_len + 1;
buffer.drawLabel(lang.brightness_down, length, 0);
length += brightness_down_len + 1;
}
buffer.drawLabel(config.brightness_up_key, length, 0);
length += config.brightness_up_key.len + 1;
buffer.drawLabel(" ", length - 1, 0);
if (config.brightness_up_key) |key| {
buffer.drawLabel(key, length, 0);
length += key.len + 1;
buffer.drawLabel(" ", length - 1, 0);
buffer.drawLabel(lang.brightness_up, length, 0);
length += brightness_up_len + 1;
buffer.drawLabel(lang.brightness_up, length, 0);
length += brightness_up_len + 1;
}
}
if (config.box_title) |title| {
@@ -512,7 +538,7 @@ pub fn main() !void {
buffer.drawLabel(label_txt, buffer.box_x, buffer.box_y + buffer.box_height);
}
draw_lock_state: {
if (can_access_console_dev) draw_lock_state: {
const lock_state = interop.getLockState(config.console_dev) catch {
try info_line.addMessage(lang.err_console_dev, config.error_bg, config.error_fg);
break :draw_lock_state;
@@ -533,11 +559,11 @@ pub fn main() !void {
login.draw();
password.draw();
} else {
std.time.sleep(std.time.ns_per_ms * 10);
std.Thread.sleep(std.time.ns_per_ms * 10);
update = buffer.cascade();
if (!update) {
std.time.sleep(std.time.ns_per_s * 7);
std.Thread.sleep(std.time.ns_per_s * 7);
auth_fails = 0;
}
}
@@ -557,11 +583,7 @@ pub fn main() !void {
if (config.animation_timeout_sec > 0 and tv.tv_sec - tv_zero.tv_sec > config.animation_timeout_sec) {
animation_timed_out = true;
switch (config.animation) {
.none => {},
.doom => doom.deinit(),
.matrix => matrix.deinit(),
}
animation.deinit();
}
} else if (config.bigclock != .none and config.clock == null) {
var tv: interop.system_time.timeval = undefined;
@@ -611,21 +633,14 @@ pub fn main() !void {
}
}
}
} else if (pressed_key == brightness_down_key or pressed_key == brightness_up_key) {
const cmd = if (pressed_key == brightness_down_key) config.brightness_down_cmd else config.brightness_up_cmd;
var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", cmd }, allocator);
brightness.stdout_behavior = .Ignore;
brightness.stderr_behavior = .Ignore;
handle_brightness_cmd: {
const process_result = brightness.spawnAndWait() catch {
break :handle_brightness_cmd;
};
if (process_result.Exited != 0) {
try info_line.addMessage(lang.err_brightness_change, config.error_bg, config.error_fg);
}
}
} else if (brightness_down_key != null and pressed_key == brightness_down_key.?) {
adjustBrightness(allocator, config.brightness_down_cmd) catch {
try info_line.addMessage(lang.err_brightness_change, config.error_bg, config.error_fg);
};
} else if (brightness_up_key != null and pressed_key == brightness_up_key.?) {
adjustBrightness(allocator, config.brightness_up_cmd) catch {
try info_line.addMessage(lang.err_brightness_change, config.error_bg, config.error_fg);
};
}
},
termbox.TB_KEY_CTRL_C => run = false,
@@ -673,7 +688,17 @@ pub fn main() !void {
update = true;
},
termbox.TB_KEY_ENTER => {
termbox.TB_KEY_ENTER => authenticate: {
if (!config.allow_empty_password and password.text.items.len == 0) {
try info_line.addMessage(lang.err_empty_password, config.error_bg, config.error_fg);
InfoLine.clearRendered(allocator, buffer) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
};
info_line.label.draw();
_ = termbox.tb_present();
break :authenticate;
}
try info_line.addMessage(lang.authenticating, config.bg, config.fg);
InfoLine.clearRendered(allocator, buffer) catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
@@ -689,7 +714,7 @@ pub fn main() !void {
.user = login.text.items,
.session_index = session.label.current,
};
ini.writeFromStruct(save_data, file.writer(), null, true, .{}) catch break :save_last_settings;
ini.writeFromStruct(save_data, file.writer(), null, .{}) catch break :save_last_settings;
// Delete previous save file if it exists
if (migrator.maybe_save_file) |path| std.fs.cwd().deleteFile(path) catch {};
@@ -704,13 +729,30 @@ pub fn main() !void {
const password_text = try allocator.dupeZ(u8, password.text.items);
defer allocator.free(password_text);
// Give up TTY
_ = termbox.tb_shutdown();
session_pid = try std.posix.fork();
if (session_pid == 0) {
const current_environment = session.label.list.items[session.label.current];
auth.authenticate(config, current_environment, login_text, password_text) catch |err| {
const auth_options = auth.AuthOptions{
.tty = config.tty,
.service_name = config.service_name,
.path = config.path,
.session_log = config.session_log,
.xauth_cmd = config.xauth_cmd,
.setup_cmd = config.setup_cmd,
.login_cmd = config.login_cmd,
.x_cmd = config.x_cmd,
.session_pid = session_pid,
};
// Signal action to give up control on the TTY
const tty_control_transfer_act = std.posix.Sigaction{
.handler = .{ .handler = &ttyControlTransferSignalHandler },
.mask = std.posix.empty_sigset,
.flags = 0,
};
std.posix.sigaction(std.posix.SIG.CHLD, &tty_control_transfer_act, null);
auth.authenticate(auth_options, current_environment, login_text, password_text) catch |err| {
shared_err.writeError(err);
std.process.exit(1);
};
@@ -721,12 +763,9 @@ pub fn main() !void {
session_pid = -1;
}
// Take back TTY
// Take back control of the TTY
_ = termbox.tb_init();
_ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_NORMAL);
// Reinitialise buffer to avoid use after free
buffer = TerminalBuffer.init(config, labels_max_length, random);
_ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_TRUECOLOR);
const auth_err = shared_err.readError();
if (auth_err) |err| {
@@ -744,6 +783,11 @@ pub fn main() !void {
try info_line.addMessage(lang.logout, config.bg, config.fg);
}
// Clear the TTY because termbox2 doesn't properly do it
const capability = termbox.global.caps[termbox.TB_CAP_CLEAR_SCREEN];
const capability_slice = capability[0..std.mem.len(capability)];
_ = try std.posix.write(termbox.global.ttyfd, capability_slice);
try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios);
if (auth_fails < config.auth_fails) _ = termbox.tb_clear();
@@ -799,6 +843,98 @@ pub fn main() !void {
}
}
fn addOtherEnvironment(session: *Session, lang: Lang, display_server: DisplayServer, exec: ?[]const u8) !void {
const name = switch (display_server) {
.shell => lang.shell,
.xinitrc => lang.xinitrc,
else => unreachable,
};
try session.addEnvironment(.{
.entry_ini = null,
.name = name,
.xdg_session_desktop = null,
.xdg_desktop_names = null,
.cmd = exec orelse "",
.specifier = switch (display_server) {
.wayland => lang.wayland,
.x11 => lang.x11,
else => lang.other,
},
.display_server = display_server,
});
}
fn crawl(session: *Session, lang: Lang, path: []const u8, display_server: DisplayServer) !void {
var iterable_directory = std.fs.openDirAbsolute(path, .{ .iterate = true }) catch return;
defer iterable_directory.close();
var iterator = iterable_directory.iterate();
while (try iterator.next()) |item| {
if (!std.mem.eql(u8, std.fs.path.extension(item.name), ".desktop")) continue;
const entry_path = try std.fmt.allocPrint(session.label.allocator, "{s}/{s}", .{ path, item.name });
defer session.label.allocator.free(entry_path);
var entry_ini = Ini(Entry).init(session.label.allocator);
_ = try entry_ini.readFileToStruct(entry_path, .{
.fieldHandler = null,
.comment_characters = "#",
});
errdefer entry_ini.deinit();
var xdg_session_desktop: []const u8 = undefined;
const maybe_desktop_names = entry_ini.data.@"Desktop Entry".DesktopNames;
if (maybe_desktop_names) |desktop_names| {
xdg_session_desktop = std.mem.sliceTo(desktop_names, ';');
} else {
// if DesktopNames is empty, we'll take the name of the session file
xdg_session_desktop = std.fs.path.stem(item.name);
}
// Prepare the XDG_CURRENT_DESKTOP environment variable here
const entry = entry_ini.data.@"Desktop Entry";
var xdg_desktop_names: ?[:0]const u8 = null;
if (entry.DesktopNames) |desktop_names| {
for (desktop_names) |*c| {
if (c.* == ';') c.* = ':';
}
xdg_desktop_names = desktop_names;
}
const session_desktop = try session.label.allocator.dupeZ(u8, xdg_session_desktop);
errdefer session.label.allocator.free(session_desktop);
try session.addEnvironment(.{
.entry_ini = entry_ini,
.name = entry.Name,
.xdg_session_desktop = session_desktop,
.xdg_desktop_names = xdg_desktop_names,
.cmd = entry.Exec,
.specifier = switch (display_server) {
.wayland => lang.wayland,
.x11 => lang.x11,
else => lang.other,
},
.display_server = display_server,
});
}
}
fn adjustBrightness(allocator: std.mem.Allocator, cmd: []const u8) !void {
var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", cmd }, allocator);
brightness.stdout_behavior = .Ignore;
brightness.stderr_behavior = .Ignore;
handle_brightness_cmd: {
const process_result = brightness.spawnAndWait() catch {
break :handle_brightness_cmd;
};
if (process_result.Exited != 0) {
return error.BrightnessChangeFailed;
}
}
}
fn getAuthErrorMsg(err: anyerror, lang: Lang) []const u8 {
return switch (err) {
error.GetPasswordNameFailed => lang.err_pwnam,
@@ -809,6 +945,7 @@ fn getAuthErrorMsg(err: anyerror, lang: Lang) []const u8 {
error.SetUserGidFailed => lang.err_user_gid,
error.SetUserUidFailed => lang.err_user_uid,
error.ChangeDirectoryFailed => lang.err_perm_dir,
error.TtyControlTransferFailed => lang.err_tty_ctrl,
error.SetPathFailed => lang.err_path,
error.PamAccountExpired => lang.err_pam_acct_expired,
error.PamAuthError => lang.err_pam_auth,

61
src/tui/Animation.zig Normal file
View File

@@ -0,0 +1,61 @@
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});
}

23
src/tui/Cell.zig Normal file
View File

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

View File

@@ -1,8 +1,7 @@
const std = @import("std");
const builtin = @import("builtin");
const interop = @import("../interop.zig");
const utils = @import("utils.zig");
const Config = @import("../config/Config.zig");
const Cell = @import("Cell.zig");
const Random = std.Random;
@@ -10,13 +9,44 @@ const termbox = interop.termbox;
const TerminalBuffer = @This();
pub const InitOptions = struct {
fg: u32,
bg: u32,
border_fg: u32,
margin_box_h: u8,
margin_box_v: u8,
input_len: u8,
};
pub const Styling = struct {
pub const BOLD = termbox.TB_BOLD;
pub const UNDERLINE = termbox.TB_UNDERLINE;
pub const REVERSE = termbox.TB_REVERSE;
pub const ITALIC = termbox.TB_ITALIC;
pub const BLINK = termbox.TB_BLINK;
pub const HI_BLACK = termbox.TB_HI_BLACK;
pub const BRIGHT = termbox.TB_BRIGHT;
pub const DIM = termbox.TB_DIM;
};
pub const Color = struct {
pub const DEFAULT = 0x00000000;
pub const BLACK = Styling.HI_BLACK;
pub const RED = 0x00FF0000;
pub const GREEN = 0x0000FF00;
pub const YELLOW = 0x00FFFF00;
pub const BLUE = 0x000000FF;
pub const MAGENTA = 0x00FF00FF;
pub const CYAN = 0x0000FFFF;
pub const WHITE = 0x00FFFFFF;
};
random: Random,
width: usize,
height: usize,
buffer: [*]termbox.tb_cell,
fg: u16,
bg: u16,
border_fg: u16,
fg: u32,
bg: u32,
border_fg: u32,
box_chars: struct {
left_up: u32,
left_down: u32,
@@ -34,16 +64,16 @@ box_width: usize,
box_height: usize,
margin_box_v: u8,
margin_box_h: u8,
blank_cell: Cell,
pub fn init(config: Config, labels_max_length: usize, random: Random) TerminalBuffer {
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()),
.buffer = termbox.tb_cell_buffer(),
.fg = config.fg,
.bg = config.bg,
.border_fg = config.border_fg,
.fg = options.fg,
.bg = options.bg,
.border_fg = options.border_fg,
.box_chars = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) .{
.left_up = 0x250C,
.left_down = 0x2514,
@@ -66,10 +96,11 @@ pub fn init(config: Config, labels_max_length: usize, random: Random) TerminalBu
.labels_max_length = labels_max_length,
.box_x = 0,
.box_y = 0,
.box_width = (2 * config.margin_box_h) + config.input_len + 1 + labels_max_length,
.box_height = 7 + (2 * config.margin_box_v),
.margin_box_v = config.margin_box_v,
.margin_box_h = config.margin_box_h,
.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),
};
}
@@ -79,8 +110,11 @@ pub fn cascade(self: TerminalBuffer) bool {
while (y > 0) : (y -= 1) {
for (0..self.width) |x| {
const cell = self.buffer[(y - 1) * self.width + x];
const cell_under = self.buffer[y * self.width + x];
var cell: termbox.tb_cell = undefined;
var cell_under: termbox.tb_cell = undefined;
_ = termbox.tb_get_cell(@intCast(x), @intCast(y - 1), 1, &cell);
_ = termbox.tb_get_cell(@intCast(x), @intCast(y), 1, &cell_under);
const char: u8 = @truncate(cell.ch);
if (std.ascii.isWhitespace(char)) continue;
@@ -116,29 +150,27 @@ pub fn drawBoxCenter(self: *TerminalBuffer, show_borders: bool, blank_box: bool)
_ = termbox.tb_set_cell(@intCast(x1 - 1), @intCast(y2), self.box_chars.left_down, self.border_fg, self.bg);
_ = termbox.tb_set_cell(@intCast(x2), @intCast(y2), self.box_chars.right_down, self.border_fg, self.bg);
var c1 = utils.initCell(self.box_chars.top, self.border_fg, self.bg);
var c2 = utils.initCell(self.box_chars.bottom, self.border_fg, self.bg);
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| {
utils.putCell(x1 + i, y1 - 1, c1);
utils.putCell(x1 + i, y2, c2);
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| {
utils.putCell(x1 - 1, y1 + i, c1);
utils.putCell(x2, y1 + i, c2);
c1.put(x1 - 1, y1 + i);
c2.put(x2, y1 + i);
}
}
if (blank_box) {
const blank = utils.initCell(' ', self.fg, self.bg);
for (0..self.box_height) |y| {
for (0..self.box_width) |x| {
utils.putCell(x1 + x, y1 + y, blank);
self.blank_cell.put(x1 + x, y1 + y);
}
}
}
@@ -170,7 +202,7 @@ pub fn drawLabel(self: TerminalBuffer, text: []const u8, x: usize, y: usize) voi
drawColorLabel(text, x, y, self.fg, self.bg);
}
pub fn drawColorLabel(text: []const u8, x: usize, y: usize, fg: u16, bg: u16) void {
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();
@@ -193,7 +225,17 @@ pub fn drawConfinedLabel(self: TerminalBuffer, text: []const u8, x: usize, y: us
}
}
pub fn drawCharMultiple(self: TerminalBuffer, char: u8, x: usize, y: usize, length: usize) void {
const cell = utils.initCell(char, self.fg, self.bg);
for (0..length) |xx| utils.putCell(x + xx, y, cell);
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: u8 = 0;
while (utf8.nextCodepoint()) |_| i += 1;
return i;
}

View File

@@ -1,7 +1,6 @@
const std = @import("std");
const TerminalBuffer = @import("../TerminalBuffer.zig");
const generic = @import("generic.zig");
const utils = @import("../utils.zig");
const Allocator = std.mem.Allocator;
@@ -12,8 +11,8 @@ const InfoLine = @This();
const Message = struct {
width: u8,
text: []const u8,
bg: u16,
fg: u16,
bg: u32,
fg: u32,
};
label: MessageLabel,
@@ -24,15 +23,15 @@ pub fn init(allocator: Allocator, buffer: *TerminalBuffer) InfoLine {
};
}
pub fn deinit(self: InfoLine) void {
pub fn deinit(self: *InfoLine) void {
self.label.deinit();
}
pub fn addMessage(self: *InfoLine, text: []const u8, bg: u16, fg: u16) !void {
pub fn addMessage(self: *InfoLine, text: []const u8, bg: u32, fg: u32) !void {
if (text.len == 0) return;
try self.label.addItem(.{
.width = try utils.strWidth(text),
.width = try TerminalBuffer.strWidth(text),
.text = text,
.bg = bg,
.fg = fg,

View File

@@ -1,47 +1,26 @@
const std = @import("std");
const TerminalBuffer = @import("../TerminalBuffer.zig");
const enums = @import("../../enums.zig");
const ini = @import("zigini");
const Environment = @import("../../Environment.zig");
const generic = @import("generic.zig");
const Ini = @import("zigini").Ini;
const Lang = @import("../../config/Lang.zig");
const Allocator = std.mem.Allocator;
const DisplayServer = enums.DisplayServer;
const Ini = ini.Ini;
const EnvironmentLabel = generic.CyclableLabel(Environment);
const Session = @This();
pub const Environment = struct {
entry_ini: ?Ini(Entry) = null,
name: [:0]const u8 = "",
xdg_session_desktop: ?[:0]const u8 = null,
xdg_desktop_names: ?[:0]const u8 = null,
cmd: []const u8 = "",
specifier: []const u8 = "",
display_server: DisplayServer = .wayland,
};
const DesktopEntry = struct {
Exec: []const u8 = "",
Name: [:0]const u8 = "",
DesktopNames: ?[:0]u8 = null,
};
pub const Entry = struct { @"Desktop Entry": DesktopEntry = .{} };
label: EnvironmentLabel,
lang: Lang,
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, lang: Lang) Session {
pub fn init(allocator: Allocator, buffer: *TerminalBuffer) Session {
return .{
.label = EnvironmentLabel.init(allocator, buffer, drawItem),
.lang = lang,
};
}
pub fn deinit(self: Session) void {
pub fn deinit(self: *Session) void {
for (self.label.list.items) |*environment| {
if (environment.entry_ini) |*entry_ini| entry_ini.deinit();
if (environment.xdg_session_desktop) |session_desktop| self.label.allocator.free(session_desktop);
@@ -50,83 +29,8 @@ pub fn deinit(self: Session) void {
self.label.deinit();
}
pub fn addEnvironment(self: *Session, entry: DesktopEntry, xdg_session_desktop: ?[:0]const u8, display_server: DisplayServer) !void {
var xdg_desktop_names: ?[:0]const u8 = null;
if (entry.DesktopNames) |desktop_names| {
for (desktop_names) |*c| {
if (c.* == ';') c.* = ':';
}
xdg_desktop_names = desktop_names;
}
try self.label.addItem(.{
.entry_ini = null,
.name = entry.Name,
.xdg_session_desktop = xdg_session_desktop,
.xdg_desktop_names = xdg_desktop_names,
.cmd = entry.Exec,
.specifier = switch (display_server) {
.wayland => self.lang.wayland,
.x11 => self.lang.x11,
else => self.lang.other,
},
.display_server = display_server,
});
}
pub fn addEnvironmentWithIni(self: *Session, entry_ini: Ini(Entry), xdg_session_desktop: ?[:0]const u8, display_server: DisplayServer) !void {
const entry = entry_ini.data.@"Desktop Entry";
var xdg_desktop_names: ?[:0]const u8 = null;
if (entry.DesktopNames) |desktop_names| {
for (desktop_names) |*c| {
if (c.* == ';') c.* = ':';
}
xdg_desktop_names = desktop_names;
}
try self.label.addItem(.{
.entry_ini = entry_ini,
.name = entry.Name,
.xdg_session_desktop = xdg_session_desktop,
.xdg_desktop_names = xdg_desktop_names,
.cmd = entry.Exec,
.specifier = switch (display_server) {
.wayland => self.lang.wayland,
.x11 => self.lang.x11,
else => self.lang.other,
},
.display_server = display_server,
});
}
pub fn crawl(self: *Session, path: []const u8, display_server: DisplayServer) !void {
var iterable_directory = std.fs.openDirAbsolute(path, .{ .iterate = true }) catch return;
defer iterable_directory.close();
var iterator = iterable_directory.iterate();
while (try iterator.next()) |item| {
if (!std.mem.eql(u8, std.fs.path.extension(item.name), ".desktop")) continue;
const entry_path = try std.fmt.allocPrint(self.label.allocator, "{s}/{s}", .{ path, item.name });
defer self.label.allocator.free(entry_path);
var entry_ini = Ini(Entry).init(self.label.allocator);
_ = try entry_ini.readFileToStruct(entry_path, "#", null);
errdefer entry_ini.deinit();
var xdg_session_desktop: []const u8 = undefined;
const maybe_desktop_names = entry_ini.data.@"Desktop Entry".DesktopNames;
if (maybe_desktop_names) |desktop_names| {
xdg_session_desktop = std.mem.sliceTo(desktop_names, ';');
} else {
// if DesktopNames is empty, we'll take the name of the session file
xdg_session_desktop = std.fs.path.stem(item.name);
}
const session_desktop = try self.label.allocator.dupeZ(u8, xdg_session_desktop);
errdefer self.label.allocator.free(session_desktop);
try self.addEnvironmentWithIni(entry_ini, session_desktop, display_server);
}
pub fn addEnvironment(self: *Session, environment: Environment) !void {
try self.label.addItem(environment);
}
fn drawItem(label: *EnvironmentLabel, environment: Environment, x: usize, y: usize) bool {

View File

@@ -1,10 +1,9 @@
const std = @import("std");
const interop = @import("../../interop.zig");
const TerminalBuffer = @import("../TerminalBuffer.zig");
const utils = @import("../utils.zig");
const Allocator = std.mem.Allocator;
const DynamicString = std.ArrayList(u8);
const DynamicString = std.ArrayListUnmanaged(u8);
const termbox = interop.termbox;
@@ -20,10 +19,10 @@ visible_length: usize,
x: usize,
y: usize,
masked: bool,
maybe_mask: ?u8,
maybe_mask: ?u32,
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, masked: bool, maybe_mask: ?u8) Text {
const text = DynamicString.init(allocator);
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, masked: bool, maybe_mask: ?u32) Text {
const text: DynamicString = .empty;
return .{
.allocator = allocator,
@@ -40,8 +39,8 @@ pub fn init(allocator: Allocator, buffer: *TerminalBuffer, masked: bool, maybe_m
};
}
pub fn deinit(self: Text) void {
self.text.deinit();
pub fn deinit(self: *Text) void {
self.text.deinit(self.allocator);
}
pub fn position(self: *Text, x: usize, y: usize, visible_length: usize) void {
@@ -154,7 +153,7 @@ fn backspace(self: *Text) void {
fn write(self: *Text, char: u8) !void {
if (char == 0) return;
try self.text.insert(self.cursor, char);
try self.text.insert(self.allocator, self.cursor, char);
self.end += 1;
self.goRight();

View File

@@ -1,12 +1,11 @@
const std = @import("std");
const enums = @import("../../enums.zig");
const interop = @import("../../interop.zig");
const TerminalBuffer = @import("../TerminalBuffer.zig");
pub fn CyclableLabel(comptime ItemType: type) type {
return struct {
const Allocator = std.mem.Allocator;
const ItemList = std.ArrayList(ItemType);
const ItemList = std.ArrayListUnmanaged(ItemType);
const DrawItemFn = *const fn (*Self, ItemType, usize, usize) bool;
const termbox = interop.termbox;
@@ -28,7 +27,7 @@ pub fn CyclableLabel(comptime ItemType: type) type {
return .{
.allocator = allocator,
.buffer = buffer,
.list = ItemList.init(allocator),
.list = .empty,
.current = 0,
.visible_length = 0,
.x = 0,
@@ -39,8 +38,8 @@ pub fn CyclableLabel(comptime ItemType: type) type {
};
}
pub fn deinit(self: Self) void {
self.list.deinit();
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 {
@@ -54,7 +53,7 @@ pub fn CyclableLabel(comptime ItemType: type) type {
}
pub fn addItem(self: *Self, item: ItemType) !void {
try self.list.append(item);
try self.list.append(self.allocator, item);
self.current = self.list.items.len - 1;
}

View File

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