Rewrite main.c

This commit is contained in:
AnErrupTion 2023-06-16 11:31:57 +02:00
parent 97da982e48
commit f99468db0b
2 changed files with 403 additions and 1 deletions

View File

@ -19,6 +19,7 @@ pub fn build(b: *std.Build) void {
const exe = b.addExecutable(.{ const exe = b.addExecutable(.{
.name = "ly", .name = "ly",
.root_source_file = .{ .path = "src/main.zig" },
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
@ -38,7 +39,7 @@ pub fn build(b: *std.Build) void {
exe.addCSourceFile("src/draw.c", &c_args); exe.addCSourceFile("src/draw.c", &c_args);
exe.addCSourceFile("src/inputs.c", &c_args); exe.addCSourceFile("src/inputs.c", &c_args);
exe.addCSourceFile("src/login.c", &c_args); exe.addCSourceFile("src/login.c", &c_args);
exe.addCSourceFile("src/main.c", &c_args); //exe.addCSourceFile("src/main.c", &c_args);
exe.addCSourceFile("src/utils.c", &c_args); exe.addCSourceFile("src/utils.c", &c_args);
exe.addCSourceFile("dep/argoat/src/argoat.c", &c_args); exe.addCSourceFile("dep/argoat/src/argoat.c", &c_args);
exe.addCSourceFile("dep/argoat/dep/testoasterror/src/testoasterror.c", &c_args); exe.addCSourceFile("dep/argoat/dep/testoasterror/src/testoasterror.c", &c_args);

401
src/main.zig Normal file
View File

@ -0,0 +1,401 @@
const std = @import("std");
const c = @cImport({
@cInclude("argoat.h");
@cInclude("configator.h");
@cInclude("dragonfail.h");
@cInclude("termbox.h");
@cInclude("draw.h");
@cInclude("inputs.h");
@cInclude("login.h");
@cInclude("utils.h");
@cInclude("config.h");
});
// Compile-time settings
const LY_VERSION = "0.7.0";
const MAX_AUTH_FAILS = 10;
// Main allocator for Ly
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
// Ly general and language configuration
var config: c.struct_config = undefined;
var lang: c.struct_lang = undefined;
comptime {
@export(config, .{ .name = "config" });
@export(lang, .{ .name = "lang" });
}
// Main function
pub fn main() !void {
// Initialize structs
var config_ptr = try allocator.create(c.struct_config);
defer allocator.destroy(config_ptr);
var lang_ptr = try allocator.create(c.struct_lang);
defer allocator.destroy(lang_ptr);
config = config_ptr.*;
lang = lang_ptr.*;
// Load configuration
c.config_defaults();
c.lang_defaults();
// Initialize error library
log_init(c.dgn_init());
// Parse command line arguments
var config_path: [*c]u8 = undefined;
const sprigs = [_]c.struct_argoat_sprig{
.{ .flag = undefined, .pars_max = 0, .data = undefined, .func = undefined },
.{ .flag = "config", .pars_max = 0, .data = config_path, .func = arg_config },
.{ .flag = "c", .pars_max = 0, .data = config_path, .func = arg_config },
.{ .flag = "help", .pars_max = 0, .data = undefined, .func = arg_help },
.{ .flag = "h", .pars_max = 0, .data = undefined, .func = arg_help },
.{ .flag = "version", .pars_max = 0, .data = undefined, .func = arg_version },
.{ .flag = "v", .pars_max = 0, .data = undefined, .func = arg_version },
};
var args = c.struct_argoat{
.sprigs = &sprigs,
.sprigs_count = sprigs.len,
.unflagged = undefined,
.unflagged_count = 0,
.unflagged_max = 0,
};
var process_args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, process_args);
const argv = try allocator.allocSentinel(?[*:0]u8, process_args.len, null);
defer allocator.free(argv);
for (process_args, 0..) |arg, i| {
argv[i] = (try allocator.dupeZ(u8, arg)).ptr;
}
c.argoat_graze(&args, @intCast(c_int, process_args.len), argv.ptr);
// Initialize inputs
var desktop = try allocator.create(c.struct_desktop);
defer allocator.destroy(desktop);
var username = try allocator.create(c.struct_text);
defer allocator.destroy(username);
var password = try allocator.create(c.struct_text);
defer allocator.destroy(password);
c.input_desktop(desktop);
c.input_text(username, config.max_login_len);
c.input_text(password, config.max_password_len);
if (c.dgn_catch() != 0) {
c.config_free();
c.lang_free();
std.os.exit(1);
}
c.config_load(config_path);
c.lang_load();
c.desktop_load(desktop);
c.load(desktop, username);
// Start termbox
_ = c.tb_init();
_ = c.tb_select_output_mode(c.TB_OUTPUT_NORMAL);
c.tb_clear();
// Initialize visible elements
var event = try allocator.create(c.struct_tb_event);
defer allocator.destroy(event);
var buffer = try allocator.create(c.struct_term_buf);
defer allocator.destroy(buffer);
// Place the cursor on the login field if there is no saved username
// If there is, place the curser on the password field
var active_input: u8 = 0;
if (config.default_input == c.LOGIN_INPUT and username.text != username.end) {
active_input = c.PASSWORD_INPUT;
} else {
active_input = config.default_input;
}
// Initialize drawing code
c.draw_init(buffer);
// draw_box() and position_input() are called because they need to be
// called before the switch case for the cursor to be positioned correctly
c.draw_box(buffer);
c.position_input(buffer, desktop, username, password);
switch (active_input) {
c.SESSION_SWITCH => {
c.handle_desktop(desktop, event);
},
c.LOGIN_INPUT => {
c.handle_text(username, event);
},
c.PASSWORD_INPUT => {
c.handle_text(password, event);
},
else => unreachable,
}
if (config.animate) {
c.animate_init(buffer);
if (c.dgn_catch() != 0) {
config.animate = false;
c.dgn_reset();
}
}
// Initialize state information
var err: c_int = 0;
var run = true;
var update = true;
var reboot = false;
var shutdown = false;
var auth_fails: u8 = 0;
c.switch_tty(buffer);
// Main loop
while (run) {
if (update) {
if (auth_fails < MAX_AUTH_FAILS) {
switch (active_input) {
c.SESSION_SWITCH => {
c.handle_desktop(desktop, event);
},
c.LOGIN_INPUT => {
c.handle_text(username, event);
},
c.PASSWORD_INPUT => {
c.handle_text(password, event);
},
else => unreachable,
}
c.tb_clear();
c.animate(buffer);
c.draw_bigclock(buffer);
c.draw_box(buffer);
c.draw_clock(buffer);
c.draw_labels(buffer);
if (!config.hide_f1_commands) {
c.draw_f_commands();
}
c.draw_lock_state(buffer);
c.position_input(buffer, desktop, username, password);
c.draw_desktop(desktop);
c.draw_input(username);
c.draw_input_mask(password);
update = config.animate;
} else {
std.time.sleep(10000000); // Sleep 0.01 seconds
update = c.cascade(buffer, &auth_fails);
}
c.tb_present();
}
var timeout: c_int = -1;
if (config.animate) {
timeout = config.min_refresh_delta;
} else {
// TODO: Use the Zig standard library directly
var time = try allocator.create(std.os.linux.timeval);
defer allocator.destroy(time);
_ = std.os.linux.gettimeofday(time, undefined);
if (config.bigclock) {
timeout = @intCast(c_int, (60 - @mod(time.tv_sec, 60)) * 1000 - @divTrunc(time.tv_usec, 1000) + 1);
} else if (config.clock != undefined) {
timeout = @intCast(c_int, 1000 - @divTrunc(time.tv_usec, 1000) + 1);
}
}
if (timeout == -1) {
err = c.tb_poll_event(event);
} else {
err = c.tb_peek_event(event, timeout);
}
if (err < 0) {
continue;
}
if (event.type == c.TB_EVENT_KEY) {
switch (event.key) {
c.TB_KEY_F1 => {
shutdown = true;
run = false;
},
c.TB_KEY_F2 => {
reboot = true;
run = false;
},
c.TB_KEY_CTRL_C => {
run = false;
},
c.TB_KEY_CTRL_U => {
if (active_input > c.SESSION_SWITCH) {
switch (active_input) {
c.LOGIN_INPUT => {
c.input_text_clear(username);
},
c.PASSWORD_INPUT => {
c.input_text_clear(password);
},
else => unreachable,
}
update = true;
}
},
c.TB_KEY_CTRL_K, c.TB_KEY_ARROW_UP => {
if (active_input > c.SESSION_SWITCH) {
active_input -= 1;
update = true;
}
},
c.TB_KEY_CTRL_J, c.TB_KEY_ARROW_DOWN => {
if (active_input < c.PASSWORD_INPUT) {
active_input += 1;
update = true;
}
},
c.TB_KEY_TAB => {
active_input += 1;
if (active_input > c.PASSWORD_INPUT) {
active_input = c.SESSION_SWITCH;
}
update = true;
},
c.TB_KEY_ENTER => {
c.save(desktop, username);
c.auth(desktop, username, password, buffer);
if (c.dgn_catch() != 0) {
auth_fails += 1;
// Move focus back to password input
active_input = c.PASSWORD_INPUT;
if (c.dgn_output_code() != c.DGN_PAM) {
buffer.info_line = c.dgn_output_log();
}
if (config.blank_password) {
c.input_text_clear(password);
}
c.dgn_reset();
} else {
buffer.info_line = lang.logout;
}
c.load(desktop, username);
// Reset cursor to its normal state
_ = std.ChildProcess.exec(.{ .argv = &[_][]const u8{ "/sbin/tput", "cnorm" }, .allocator = allocator }) catch return;
update = true;
},
else => {
update = true;
},
}
}
}
// Stop termbox
c.tb_shutdown();
// Free inputs
c.input_desktop_free(desktop);
c.input_text_free(username);
c.input_text_free(password);
c.free_hostname();
// Unload configuration
c.draw_free(buffer);
c.lang_free();
if (shutdown) {
var shutdown_cmd = try std.fmt.allocPrint(allocator, "{s}", .{config.shutdown_cmd});
// This will never be freed! But it's fine, we're shutting down the system anyway
defer allocator.free(shutdown_cmd);
c.config_free();
std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", shutdown_cmd }) catch return;
} else if (reboot) {
var restart_cmd = try std.fmt.allocPrint(allocator, "{s}", .{config.restart_cmd});
// This will never be freed! But it's fine, we're rebooting the system anyway
defer allocator.free(restart_cmd);
c.config_free();
std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", restart_cmd }) catch return;
} else {
c.config_free();
}
}
// Low-level error messages
fn log_init(log: [*c][*c]u8) void {
log[c.DGN_OK] = lang.err_dgn_oob;
log[c.DGN_NULL] = lang.err_null;
log[c.DGN_ALLOC] = lang.err_alloc;
log[c.DGN_BOUNDS] = lang.err_bounds;
log[c.DGN_DOMAIN] = lang.err_domain;
log[c.DGN_MLOCK] = lang.err_mlock;
log[c.DGN_XSESSIONS_DIR] = lang.err_xsessions_dir;
log[c.DGN_XSESSIONS_OPEN] = lang.err_xsessions_open;
log[c.DGN_PATH] = lang.err_path;
log[c.DGN_CHDIR] = lang.err_chdir;
log[c.DGN_PWNAM] = lang.err_pwnam;
log[c.DGN_USER_INIT] = lang.err_user_init;
log[c.DGN_USER_GID] = lang.err_user_gid;
log[c.DGN_USER_UID] = lang.err_user_uid;
log[c.DGN_PAM] = lang.err_pam;
log[c.DGN_HOSTNAME] = lang.err_hostname;
}
// Sets the Ly configuration path
fn arg_config(data: ?*anyopaque, pars: [*c][*c]u8, pars_count: c_int) callconv(.C) void {
_ = pars_count;
_ = pars;
_ = data;
// TODO
//@ptrCast([*c][*c]u8, @alignCast(8, data.?)).* = pars.*;
}
// Shows the help message
fn arg_help(data: ?*anyopaque, pars: [*c][*c]u8, pars_count: c_int) callconv(.C) void {
_ = pars_count;
_ = pars;
_ = data;
std.debug.print("If you want to configure Ly, please check the config file, usually located at /etc/ly/config.ini.\n", .{});
}
// Shows the version of Ly
fn arg_version(data: ?*anyopaque, pars: [*c][*c]u8, pars_count: c_int) callconv(.C) void {
_ = pars_count;
_ = pars;
_ = data;
std.debug.print("Ly version {s}.\n", .{LY_VERSION});
}