From 92e1f083a1f8c8bf0c0022e75b7f5532b87fcf5a Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Mon, 11 Dec 2023 18:14:52 +0100 Subject: [PATCH] Commit unfinished full rewrite (Zig 0.11.0) What needs to be dealt with: - Matrix animation - Authentication part - Testing on actual TTY (not just virtual console) Signed-off-by: AnErrupTion --- build.zig | 39 +- build.zig.zon | 12 +- dep/configator/.gitignore | 2 - dep/configator/license.md | 13 - dep/configator/readme.md | 95 --- dep/configator/res/test.ini | 37 - dep/configator/src/configator.c | 309 ------- dep/configator/src/configator.h | 35 - dep/configator/src/example.c | 71 -- dep/dragonfail/example/dragonfail_error.h | 22 - dep/dragonfail/example/example.c | 46 - dep/dragonfail/license.md | 13 - dep/dragonfail/readme.md | 77 -- dep/dragonfail/src/dragonfail.c | 106 --- dep/dragonfail/src/dragonfail.h | 22 - dep/dragonfail/src/dragonfail_private.h | 14 - dep/termbox_next/src/termbox.c | 22 +- res/config.ini | 35 +- res/lang/cat.ini | 4 +- res/lang/cs.ini | 4 +- res/lang/de.ini | 4 +- res/lang/en.ini | 4 +- res/lang/es.ini | 4 +- res/lang/fr.ini | 4 +- res/lang/it.ini | 4 +- res/lang/pl.ini | 4 +- res/lang/pt.ini | 4 +- res/lang/pt_BR.ini | 4 +- res/lang/ro.ini | 4 +- res/lang/ru.ini | 4 +- res/lang/sr.ini | 4 +- res/lang/sv.ini | 4 +- res/lang/tr.ini | 4 +- res/lang/uk.ini | 4 +- src/animations/Doom.zig | 84 ++ src/animations/Matrix.zig | 189 ++++ src/auth.zig | 65 ++ src/bigclock.h | 146 ---- src/bigclock.zig | 140 +++ src/config.h | 102 --- src/config.zig | 424 --------- src/config/Config.zig | 97 +++ src/config/ConfigReader.zig | 47 + src/config/Lang.zig | 99 +++ src/dragonfail_error.h | 27 - src/draw.c | 997 ---------------------- src/draw.h | 92 -- src/enums.zig | 18 + src/inputs.c | 285 ------- src/inputs.h | 57 -- src/interop.zig | 124 ++- src/login.c | 715 ---------------- src/login.h | 13 - src/main.zig | 738 +++++++++------- src/tui/TerminalBuffer.zig | 169 ++++ src/tui/components/Desktop.zig | 170 ++++ src/tui/components/Text.zig | 122 +++ src/tui/utils.zig | 12 + src/utils.c | 78 -- src/utils.h | 13 - src/utils.zig | 114 --- 61 files changed, 1851 insertions(+), 4320 deletions(-) delete mode 100644 dep/configator/.gitignore delete mode 100644 dep/configator/license.md delete mode 100644 dep/configator/readme.md delete mode 100644 dep/configator/res/test.ini delete mode 100644 dep/configator/src/configator.c delete mode 100644 dep/configator/src/configator.h delete mode 100644 dep/configator/src/example.c delete mode 100644 dep/dragonfail/example/dragonfail_error.h delete mode 100644 dep/dragonfail/example/example.c delete mode 100644 dep/dragonfail/license.md delete mode 100644 dep/dragonfail/readme.md delete mode 100644 dep/dragonfail/src/dragonfail.c delete mode 100644 dep/dragonfail/src/dragonfail.h delete mode 100644 dep/dragonfail/src/dragonfail_private.h create mode 100644 src/animations/Doom.zig create mode 100644 src/animations/Matrix.zig create mode 100644 src/auth.zig delete mode 100644 src/bigclock.h create mode 100644 src/bigclock.zig delete mode 100644 src/config.h delete mode 100644 src/config.zig create mode 100644 src/config/Config.zig create mode 100644 src/config/ConfigReader.zig create mode 100644 src/config/Lang.zig delete mode 100644 src/dragonfail_error.h delete mode 100644 src/draw.c delete mode 100644 src/draw.h create mode 100644 src/enums.zig delete mode 100644 src/inputs.c delete mode 100644 src/inputs.h delete mode 100644 src/login.c delete mode 100644 src/login.h create mode 100644 src/tui/TerminalBuffer.zig create mode 100644 src/tui/components/Desktop.zig create mode 100644 src/tui/components/Text.zig create mode 100644 src/tui/utils.zig delete mode 100644 src/utils.c delete mode 100644 src/utils.h delete mode 100644 src/utils.zig diff --git a/build.zig b/build.zig index c192a6b..f5e8b1c 100644 --- a/build.zig +++ b/build.zig @@ -1,6 +1,11 @@ const std = @import("std"); pub fn build(b: *std.Build) void { + const data_directory = b.option([]const u8, "data_directory", "Specify a default data directory (default is /etc/ly)"); + + const build_options = b.addOptions(); + build_options.addOption([]const u8, "data_directory", data_directory orelse "/etc/ly"); + const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); @@ -24,30 +29,26 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }); + exe.addOptions("build_options", build_options); + const ini = b.dependency("ini", .{}); exe.addModule("ini", ini.module("ini")); + const clap = b.dependency("clap", .{ .target = target, .optimize = optimize }); + exe.addModule("clap", clap.module("clap")); + exe.linkSystemLibrary("pam"); exe.linkSystemLibrary("xcb"); exe.linkLibC(); - exe.addIncludePath("src"); - exe.addIncludePath("dep/configator/src"); - exe.addIncludePath("dep/dragonfail/src"); - exe.addIncludePath("dep/termbox_next/src"); - - exe.addCSourceFile("src/draw.c", &c_args); - exe.addCSourceFile("src/inputs.c", &c_args); - exe.addCSourceFile("src/login.c", &c_args); - exe.addCSourceFile("src/utils.c", &c_args); - exe.addCSourceFile("dep/configator/src/configator.c", &c_args); - exe.addCSourceFile("dep/dragonfail/src/dragonfail.c", &c_args); - exe.addCSourceFile("dep/termbox_next/src/input.c", &c_args); - exe.addCSourceFile("dep/termbox_next/src/memstream.c", &c_args); - exe.addCSourceFile("dep/termbox_next/src/ringbuffer.c", &c_args); - exe.addCSourceFile("dep/termbox_next/src/term.c", &c_args); - exe.addCSourceFile("dep/termbox_next/src/termbox.c", &c_args); - exe.addCSourceFile("dep/termbox_next/src/utf8.c", &c_args); + exe.addIncludePath(.{ .path = "dep/termbox_next/src" }); + + exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/input.c" }, .flags = &c_args }); + exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/memstream.c" }, .flags = &c_args }); + exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/ringbuffer.c" }, .flags = &c_args }); + exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/term.c" }, .flags = &c_args }); + exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/termbox.c" }, .flags = &c_args }); + exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/utf8.c" }, .flags = &c_args }); b.installArtifact(exe); @@ -55,9 +56,7 @@ pub fn build(b: *std.Build) void { run_cmd.step.dependOn(b.getInstallStep()); - if (b.args) |args| { - run_cmd.addArgs(args); - } + if (b.args) |args| run_cmd.addArgs(args); const run_step = b.step("run", "Run the app"); run_step.dependOn(&run_cmd.step); diff --git a/build.zig.zon b/build.zig.zon index 32efe83..842afef 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,10 +1,14 @@ .{ .name = "ly", - .version = "0.7.0", + .version = "1.0.0", .dependencies = .{ .ini = .{ - .url = "https://github.com/AnErrupTion/zig-ini/archive/a40f3cae04939de0d795812a73633e0563cf3132.tar.gz", - .hash = "1220b39fc35714d235fd9633422f5ba3c4aba44f1ecaf29cf91515963f0b08a3da89", + .url = "https://github.com/AnErrupTion/zig-ini/archive/7ab77196a4dc63d1ede50e0a1af1a8325c152f2f.tar.gz", + .hash = "12204b1d133060dd0c4304d5e0fbdb33a70038118e7c112b14b5f8d176ea15cd5939", + }, + .clap = .{ + .url = "https://github.com/Hejsil/zig-clap/archive/f49b94700e0761b7514abdca0e4f0e7f3f938a93.tar.gz", + .hash = "1220f48518ce22882e102255ed3bcdb7aeeb4891f50b2cdd3bd74b5b2e24d3149ba2", }, } -} \ No newline at end of file +} diff --git a/dep/configator/.gitignore b/dep/configator/.gitignore deleted file mode 100644 index 1746e32..0000000 --- a/dep/configator/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -bin -obj diff --git a/dep/configator/license.md b/dep/configator/license.md deleted file mode 100644 index 5c93f45..0000000 --- a/dep/configator/license.md +++ /dev/null @@ -1,13 +0,0 @@ - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - Version 2, December 2004 - - Copyright (C) 2004 Sam Hocevar - - Everyone is permitted to copy and distribute verbatim or modified - copies of this license document, and changing it is allowed as long - as the name is changed. - - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/dep/configator/readme.md b/dep/configator/readme.md deleted file mode 100644 index fff8f58..0000000 --- a/dep/configator/readme.md +++ /dev/null @@ -1,95 +0,0 @@ -# Configator -Configator is a lightweight library for ini config file parsing. -This was created to make it easy following the "DRY" coding rule -without using macros, and with flexibility in mind. - -It integrates very well with the [Argoat](https://github.com/nullgemm/argoat.git) -arguments parser library, by using the same function pointers format. -This way, you can easily load settings from an ini file while overloading them -with command-line arguments if needed: the handling functions will be the same. - -Configator does not use any macro or dynamic memory allocation, -and was built in less than 350 lines of C99 code. - -## Testing -Run `make` to compile an example executable and perform basic testing - -## Using -### TL;DR -Please see `example.c` for the condensed version -(or better, read the actual documentation below). -It is a bit too long to be copied here twice... - -### Details -Include `argoat.h` and compile `argoat.c` with your code. - -Write the functions that will handle your parameters. -They will be called during the parsing process, in the order given by the user -``` -void handle_config_u8(void* data, char** value, const int pars_count) -{ - if (pars_count > 0) - { - *((uint8_t*) data) = atoi(*value); - } -} -``` - -In your `main`, declare the variables to configure. -They will be passed to the corresponding functions as `void* data` -``` - uint8_t answer = 0; -``` - -Declare the arrays of parameters by section, starting with the general section. -If you don't want to handle parameters in some section, just declare it `NULL`. -``` -struct configator_param* map_no_section = NULL; -``` - -Declare real sections parameters afterwards -``` -struct configator_param map_test_section[] = -{ - {"ping", &answer, handle_config_u8}, - {"pong", &answer, handle_config_u8}, -}; -``` - -Then group them in the map -``` -struct configator_param* map[] = -{ - map_no_section, - map_test_section -}; -``` - -And declare the sections array. Configator will execute the pointed function -with `NULL` arguments at the beginning of each detected section. -You can also declare sections with `NULL` parameters, in which case nothing -will be executed. -``` -struct configator_param sections[] = -{ - {"network_test", &answer, handle_config_u8}, -}; -``` - -Don't forget to put the right numbers in the lenght variables -``` -uint16_t map_len[] = {0, 2}; -uint16_t sections_len = 1; -``` - -Then initialize and use configator -``` -struct configator config; -config.map = map; -config.map_len = map_len; -config.sections = sections; -config.sections_len = sections_len; - -configator(&config, "config.ini"); -printf("answer = %d\n", answer); -``` diff --git a/dep/configator/res/test.ini b/dep/configator/res/test.ini deleted file mode 100644 index 1c7ac25..0000000 --- a/dep/configator/res/test.ini +++ /dev/null @@ -1,37 +0,0 @@ -sectionless = 0000 - -[sections_test] -[regular] - [left-padded] -[right-padded] - [middle-padded] -[smaller] -[ right] -[left ] -[ middle ] -[] -[ ] -[three wrong words] -[]] -[[] -[[]] - -[params_tests] -# comment - # padded comment -test = 1111 - left-padded = 2222 -right-padded = 3333 -minified=4444 -multi = 4444 3333 2222 1111 -spaces = " middle " -wrong declaration = 5555 -wrong line - -[test_section] -answer = 42 - -[network_test] -ping = 255 - -[question] diff --git a/dep/configator/src/configator.c b/dep/configator/src/configator.c deleted file mode 100644 index f433350..0000000 --- a/dep/configator/src/configator.c +++ /dev/null @@ -1,309 +0,0 @@ -#include "configator.h" -#include -#include -#include -#include -#include - -// returns the index of the searched element, or len if it can't be found -static uint16_t search(struct configator_param* config, uint16_t len, char* key) -{ - // strcmp indicator - int8_t disc; - // initial tested index - uint16_t i = len / 2; - // initial bounds (inclusive) - uint16_t k = 0; - uint16_t l = len - 1; - - // skip directly to the final check - if (len > 1) - { - // as long as a match is possible - do - { - disc = strcmp(config[i].key, key); - - if (disc == 0) - { - // found by chance - return i; - } - else if (disc > 0) - { - l = i; - i = (i + k) / 2; // floor - } - else - { - k = i; - i = (i + l) / 2 + (i + l) % 2; // ceil - } - - if (len == 2) - { - break; - } - } - while ((k+1) != l); - } - - if (len > 0) - { - // final check - disc = strcmp(config[i].key, key); - - if (disc == 0) - { - // found by dichotomy - return i; - } - } - - // not found - return len; -} - -static void configator_save_section(struct configator* config, char* line) -{ - char c; - uint16_t index; - uint16_t k = 0; // last non-space pos - uint16_t l = 0; // second last non-space pos - - // leading spaces - do - { - ++line; - c = line[0]; - } - while ((c != '\0') && isspace(c)); - - if (c == '[') - { - ++line; - c = line[0]; - } - - // trailing spaces - for (uint16_t i = 1; c != '\0'; ++i) - { - if ((c != ']') && !isspace(c)) - { - // we use two variables to avoid - // counting the ending ']' - l = k + 1; // we *must* increment here - k = i; - } - - c = line[i]; - } - - // terminator - line[l] = '\0'; - - if (l == 0) - { - return; - } - - // saving - strncpy(config->section, line, l + 1); - - // searching - index = search( - config->sections, - config->sections_len, - config->section); - -#ifdef CONFIGATOR_DEBUG - printf("[%s]\n", line); -#endif - - // calling the function - if (index != config->sections_len) - { - config->current_section = index + 1; - - if (config->sections[index].handle != NULL) - { - config->sections[index].handle( - config->sections[index].data, - NULL, - 0); - } - } -} - -static void configator_save_param(struct configator* config, char* line) -{ - char c; - uint16_t index; - uint16_t i = 0; - uint16_t k = 0; - - // leading chars - do - { - ++i; - c = line[i]; - } - while ((c != '\0') && (c != '=') && !isspace(c)); - - // empty line - if (c == '\0') - { - config->param[0] = '\0'; - config->value[0] = '\0'; - return; - } - - // end of the param - k = i; - - // spaces before next char if any - while ((c != '\0') && isspace(c)) - { - ++i; - c = line[i]; - } - - // that next char must be '=' - if (c != '=') - { - config->param[0] = '\0'; - config->value[0] = '\0'; - return; - } - else - { - ++i; - c = line[i]; - } - - // spaces after '=' - while ((c != '\0') && isspace(c)) - { - ++i; - c = line[i]; - } - - line[k] = '\0'; - strncpy(config->param, line, k + 1); - strncpy(config->value, line + i, strlen(line + i) + 1); - - // searching - if ((config->current_section == 0) && (config->map_len[0] == 0)) - { - return; - } - - index = search( - config->map[config->current_section], - config->map_len[config->current_section], - config->param); - -#ifdef CONFIGATOR_DEBUG - printf("%s = \"%s\"\n", config->param, config->value); -#endif - - // calling the function - if ((index != config->map_len[config->current_section]) - && (config->map[config->current_section][index].handle != NULL)) - { - char* tmp = (char*) config->value; - config->map[config->current_section][index].handle( - config->map[config->current_section][index].data, - &(tmp), - 1); - } -} - -static void configator_read(FILE* fp, char* line) -{ - int c = fgetc(fp); - uint16_t i = 0; - uint16_t k = 0; - - if (c == EOF) - { - line[0] = '\0'; - return; - } - - while ((c != '\n') && (c != EOF)) - { - if ((i < (CONFIGATOR_MAX_LINE + 1)) // maximum len - && ((i > 0) || !isspace(c))) // skips leading spaces - { - // used to trim trailing spaces - // and to terminate overflowing string - if (!isspace(c)) - { - k = i; - } - - line[i] = c; - ++i; - } - - c = fgetc(fp); - } - - if (i == (CONFIGATOR_MAX_LINE + 1)) - { - line[k] = '\0'; - } - else - { - line[k + 1] = '\0'; - } -} - -int configator(struct configator* config, const char* path) -{ - FILE* fp = fopen(path, "r"); - - if (fp == NULL) - { - return -1; - } - - config->section[0] = '\0'; - config->param[0] = '\0'; - config->value[0] = '\0'; - config->current_section = 0; - - // event loop - char line[CONFIGATOR_MAX_LINE + 1]; - - while (1) - { - configator_read(fp, line); - - // end of file - if (feof(fp)) - { - break; - } - // comment - else if (line[0] == '#') - { - continue; - } - // section - else if ((line[0] == '[') && (line[strlen(line) - 1] == ']')) - { - configator_save_section(config, line); - } - // param - else - { - configator_save_param(config, line); - } - } - - fclose(fp); - - return 0; -} diff --git a/dep/configator/src/configator.h b/dep/configator/src/configator.h deleted file mode 100644 index 58a7121..0000000 --- a/dep/configator/src/configator.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef H_CONFIGATOR -#define H_CONFIGATOR - -#include - -#define CONFIGATOR_MAX_LINE 80 - -#if 0 -#define CONFIGATOR_DEBUG -#endif - -struct configator_param -{ - char* key; - void* data; - void (*handle)(void* data, char** value, const int pars_count); -}; - -struct configator -{ - char section[CONFIGATOR_MAX_LINE]; - char param[CONFIGATOR_MAX_LINE]; - char value[CONFIGATOR_MAX_LINE]; - uint16_t current_section; - - struct configator_param** map; - struct configator_param* sections; - - uint16_t* map_len; - uint16_t sections_len; -}; - -int configator(struct configator* config, const char* path); - -#endif diff --git a/dep/configator/src/example.c b/dep/configator/src/example.c deleted file mode 100644 index f98a67e..0000000 --- a/dep/configator/src/example.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "configator.h" -#include -#include -#include -#include - -void handle_config_u8(void* data, char** value, const int pars_count) -{ - if (pars_count > 0) - { - *((uint8_t*) data) = atoi(*value); - } -} - -void handle_question(void* data, char** value, const int pars_count) -{ - *((uint8_t*) data) = 23; -} - -int main(int argc, char** argv) -{ - uint8_t answer = 0; - uint8_t question = 0; - - // parameters, grouped in sections - struct configator_param* map_no_section = NULL; - struct configator_param* map_question_section = NULL; - struct configator_param map_test_section[] = - { - {"aaabbb", &answer, handle_config_u8}, - {"aabbaa", &answer, handle_config_u8}, - {"answer", &answer, handle_config_u8}, - {"cccccc", &answer, handle_config_u8}, - {"cccddd", &answer, handle_config_u8}, - {"daaaaa", &answer, handle_config_u8}, - {"ddaaaa", &answer, handle_config_u8}, - {"eeeeee", &answer, handle_config_u8} - }; - struct configator_param* map[] = - { - map_no_section, - map_question_section, - map_test_section - }; - - // sections (used to execute functions at sections start) - struct configator_param sections[] = - { - {"question", &question, handle_question}, - {"test_section", NULL, NULL}, - }; - - // number of parameters, by section - uint16_t map_len[] = {0, 0, 8}; - // number of sections - uint16_t sections_len = 2; - - // configator object - struct configator config; - config.map = map; - config.map_len = map_len; - config.sections = sections; - config.sections_len = sections_len; - - // execute configuration - configator(&config, "config.ini"); - printf("question = %d\n", question); - printf("answer = %d\n", answer); - - return 0; -} diff --git a/dep/dragonfail/example/dragonfail_error.h b/dep/dragonfail/example/dragonfail_error.h deleted file mode 100644 index ed087cb..0000000 --- a/dep/dragonfail/example/dragonfail_error.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef H_DRAGONFAIL_ERROR -#define H_DRAGONFAIL_ERROR - -enum dgn_error -{ - DGN_OK, // do not remove - - DGN_NULL, - DGN_ALLOC, - DGN_BOUNDS, - DGN_DOMAIN, - - DGN_SIZE, // do not remove -}; - -//#define DRAGONFAIL_SKIP -#define DRAGONFAIL_BASIC_LOG -#define DRAGONFAIL_THROW_BASIC_LOG -#define DRAGONFAIL_THROW_DEBUG_LOG -//#define DRAGONFAIL_ABORT - -#endif diff --git a/dep/dragonfail/example/example.c b/dep/dragonfail/example/example.c deleted file mode 100644 index fdc8b9f..0000000 --- a/dep/dragonfail/example/example.c +++ /dev/null @@ -1,46 +0,0 @@ -#include -#include "dragonfail.h" - -int div(int num, int den) -{ - if (den == 0) - { - dgn_throw(DGN_DOMAIN); - return 0; - } - - return num / den; -} - -void log_init(char** log) -{ - log[DGN_OK] = "out-of-bounds log message"; // special case - log[DGN_NULL] = "null pointer"; - log[DGN_ALLOC] = "failed memory allocation"; - log[DGN_BOUNDS] = "out-of-bounds index"; - log[DGN_DOMAIN] = "invalid domain"; -} - -int main() -{ - log_init(dgn_init()); - - int i; - int q; - - for (i = -2; i < 3; ++i) - { - q = div(42, i); - - if (dgn_catch()) - { - printf("skipping division by zero\n"); - dgn_reset(); - continue; - } - - printf("42/%d = %d\n", i, q); - } - - return 0; -} diff --git a/dep/dragonfail/license.md b/dep/dragonfail/license.md deleted file mode 100644 index 5c93f45..0000000 --- a/dep/dragonfail/license.md +++ /dev/null @@ -1,13 +0,0 @@ - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - Version 2, December 2004 - - Copyright (C) 2004 Sam Hocevar - - Everyone is permitted to copy and distribute verbatim or modified - copies of this license document, and changing it is allowed as long - as the name is changed. - - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/dep/dragonfail/readme.md b/dep/dragonfail/readme.md deleted file mode 100644 index 9bd8b8a..0000000 --- a/dep/dragonfail/readme.md +++ /dev/null @@ -1,77 +0,0 @@ -# Dragonfail -Dragonfail is a simple library providing basic error handling functionnalities. -It was designed to be as lightweight as possible, and can be completely disabled -with only one `#define` (more on that later). - -Dragonfail was designed to be fast and uses inline functions exclusively. These -calls modify a global context which is not directly accessible by the programmer. - -All the error codes must be written in an enum in **your** `dragonfail_error.h` file. -Because of this rather unusual architecture, the file must be found by the compiler -when it is processing `dragonfail.c` (in addition to your own source code of course). - -## Testing -Run `make` to compile an example, and `make run` to execute it. - -## Defines -This header can also contain some `#define` to modify dragonfail's behaviour: - - `DRAGONFAIL_SKIP` completely disables the whole library, making it completely - disappear from the binary (unless your compiler is a massive douche). - - `DRAGONFAIL_BASIC_LOG` enables the `dgn_basic_log()` function calls - - `DRAGONFAIL_THROW_BASIC_LOG` makes `dgn_throw()` call `dgn_basic_log()` automatically - - `DRAGONFAIL_THROW_DEBUG_LOG` also prints the file and line in which - `dgn_throw()` is called (you don't even need to compile with symbols - because this is achieved the smart way using simple C99 macros) - - `DRAGONFAIL_ABORT` makes `dgn_throw()` call `abort()` - -Again, these `#define` must be placed in **your** `dragonfail_error.h` file. - -## Using -### TL;DR -see the `example` folder :) - -### Documentation -``` -char** dgn_init(); -``` -This intializes the context to `DGN_OK` (no error) and returns the array of strings -you can fill with log messages corresponding to the errors you added in the enum. - -``` -void dgn_reset(); -``` -This resets the context to `DGN_OK`. - -``` -void dgn_basic_log(); -``` -This prints the message corresponding to the current error to stderr. - -``` -void dgn_throw(enum dgn_error new_code); -``` -This sets the error to the given code. - -``` -char dgn_catch(); -``` -This returns true if the context currently holds an error - -## Why is the architecture so strange? -The dragonfail context is global (extern) but really *implemented* in `dragonfail.c`. -Its type depends on the size of the enum so it is *declared* in `dragonfail_private.h`: -this way we can include the user's `dragonfail_error.h` and get `DGN_SIZE`. - -The inline functions need to access this context and **we want it private**, so we can't -*implement* them directly in the header as a lot of people seem to appreciate. Instead, -we will *declare* them here, and put the *implementations* in `dragonfail.c`: this way -we can access the global context without including its declaration, because it is -implemented here as well. - -When you include `dragonfail.h`, you get access to the inline functions declarations -and thanks to this design any compiler will do the rest of the job automatically. Yes, -this whole thing is useless and over-engineered. And yes, I had fun doing it... - -## Greetings -Jinjer for the cool music \m/ -Haiku developers for indirectly giving me the idea diff --git a/dep/dragonfail/src/dragonfail.c b/dep/dragonfail/src/dragonfail.c deleted file mode 100644 index 2d8404c..0000000 --- a/dep/dragonfail/src/dragonfail.c +++ /dev/null @@ -1,106 +0,0 @@ -#include "dragonfail.h" -#include "dragonfail_private.h" -#include "dragonfail_error.h" - -#ifdef DRAGONFAIL_BASIC_LOG -#include -#endif - -#ifdef DRAGONFAIL_ABORT -#include -#endif - -// extern -struct dgn dgn; - -inline char** dgn_init() -{ - #ifndef DRAGONFAIL_SKIP - dgn.error = DGN_OK; - return dgn.log; - #else - return NULL; - #endif -} - -inline void dgn_reset() -{ - #ifndef DRAGONFAIL_SKIP - dgn.error = DGN_OK; - #endif -} - -inline void dgn_basic_log() -{ - #ifdef DRAGONFAIL_BASIC_LOG - #ifndef DRAGONFAIL_SKIP - if (dgn.error < DGN_SIZE) - { - fprintf(stderr, "%s\n", dgn.log[dgn.error]); - } - else - { - fprintf(stderr, "%s\n", dgn.log[0]); - } - #endif - #endif -} - -inline char* dgn_output_log() -{ - if (dgn.error < DGN_SIZE) - { - return dgn.log[dgn.error]; - } - else - { - return dgn.log[0]; - } -} - -enum dgn_error dgn_output_code() -{ - return dgn.error; -} - -#ifdef DRAGONFAIL_THROW_DEBUG_LOG -inline void dgn_throw_extra( - enum dgn_error new_code, - const char* file, - unsigned int line) -#else -inline void dgn_throw( - enum dgn_error new_code) -#endif -{ - #ifndef DRAGONFAIL_SKIP - dgn.error = new_code; - - #ifdef DRAGONFAIL_THROW_BASIC_LOG - #ifdef DRAGONFAIL_BASIC_LOG - #ifdef DRAGONFAIL_THROW_DEBUG_LOG - fprintf( - stderr, - "error in %s line %u: ", - file, - line); - #endif - - dgn_basic_log(); - #endif - #endif - - #ifdef DRAGONFAIL_ABORT - abort(); - #endif - #endif -} - -inline char dgn_catch() -{ - #ifndef DRAGONFAIL_SKIP - return (dgn.error != DGN_OK); - #else - return 0; - #endif -} diff --git a/dep/dragonfail/src/dragonfail.h b/dep/dragonfail/src/dragonfail.h deleted file mode 100644 index c17e634..0000000 --- a/dep/dragonfail/src/dragonfail.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef H_DRAGONFAIL -#define H_DRAGONFAIL - -#include "dragonfail_error.h" - -#ifdef DRAGONFAIL_THROW_DEBUG_LOG - #define dgn_throw(new_code) dgn_throw_extra(new_code, DGN_FILE, DGN_LINE) - #define DGN_FILE __FILE__ - #define DGN_LINE __LINE__ - void dgn_throw_extra(enum dgn_error new_code, const char* file, unsigned int line); -#else - void dgn_throw(enum dgn_error new_code); -#endif - -char** dgn_init(); -void dgn_reset(); -void dgn_basic_log(); -char* dgn_output_log(); -enum dgn_error dgn_output_code(); -char dgn_catch(); - -#endif diff --git a/dep/dragonfail/src/dragonfail_private.h b/dep/dragonfail/src/dragonfail_private.h deleted file mode 100644 index e83c383..0000000 --- a/dep/dragonfail/src/dragonfail_private.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef H_DRAGONFAIL_PRIVATE -#define H_DRAGONFAIL_PRIVATE - -#include "dragonfail_error.h" - -struct dgn -{ - enum dgn_error error; - char* log[DGN_SIZE]; -}; - -extern struct dgn dgn; - -#endif diff --git a/dep/termbox_next/src/termbox.c b/dep/termbox_next/src/termbox.c index 72a4335..82e30fc 100644 --- a/dep/termbox_next/src/termbox.c +++ b/dep/termbox_next/src/termbox.c @@ -625,17 +625,6 @@ static void get_term_size(int* w, int* h) } } -static void update_term_size(void) -{ - struct winsize sz; - memset(&sz, 0, sizeof(sz)); - - ioctl(out_fileno, TIOCGWINSZ, &sz); - - termw = sz.ws_col; - termh = sz.ws_row; -} - static void send_attr(uint32_t fg, uint32_t bg) { #define LAST_ATTR_INIT 0xFFFFFFFF @@ -789,6 +778,17 @@ static void update_size(void) send_clear(); } +static void update_term_size(void) +{ + struct winsize sz; + memset(&sz, 0, sizeof(sz)); + + ioctl(out_fileno, TIOCGWINSZ, &sz); + + termw = sz.ws_col; + termh = sz.ws_row; +} + static int wait_fill_event(struct tb_event* event, struct timeval* timeout) { #define ENOUGH_DATA_FOR_INPUT_PARSING 128 diff --git a/res/config.ini b/res/config.ini index 2b2bcab..dc81fc1 100644 --- a/res/config.ini +++ b/res/config.ini @@ -1,18 +1,16 @@ [ly] -# Animation enabled/disabled -animate = false - # The active animation -# 0 -> PSX DOOM fire (default) -# 1 -> CMatrix -animation = 0 +# none -> Nothing (default) +# doom -> PSX DOOM fire +# matrix -> CMatrix +animation = doom # Format string for clock in top right corner (see strftime specification) -clock = +clock = %c # Enable/disable big clock -bigclock = false +bigclock = true # The character used to mask the password asterisk = * @@ -66,6 +64,7 @@ max_password_len = 255 # Input box active by default on startup +# Available inputs: session, login, password default_input = login # Load the saved desktop and username @@ -78,13 +77,19 @@ save = true save_file = /etc/ly/save -# Remove F1/F2 command hints -hide_f1_commands = false +# Remove power management command hints +hide_key_hints = false + +# Specifies the key used for shutdown (F1-F12) +shutdown_key = F1 + +# Specifies the key used for restart (F1-F12) +restart_key = F2 -# Command executed when pressing F1 +# Command executed when pressing shutdown_key shutdown_cmd = /sbin/shutdown -a now -# Command executed when pressing F2 +# Command executed when pressing restart_key restart_cmd = /sbin/shutdown -r now @@ -112,6 +117,9 @@ service_name = ly # Terminal reset command (tput is faster) term_reset_cmd = /usr/bin/tput reset +# Terminal restore cursor command +term_restore_cursor_cmd = /usr/bin/tput cnorm + # Cookie generator mcookie_cmd = /usr/bin/mcookie @@ -119,9 +127,6 @@ mcookie_cmd = /usr/bin/mcookie # Wayland setup command wayland_cmd = /etc/ly/wsetup.sh -# Add wayland specifier to session names -wayland_specifier = false - # Wayland desktop environments waylandsessions = /usr/share/wayland-sessions diff --git a/res/lang/cat.ini b/res/lang/cat.ini index 6389310..56bd49a 100755 --- a/res/lang/cat.ini +++ b/res/lang/cat.ini @@ -35,12 +35,12 @@ err_user_init = error al inicialitzar usuari err_user_uid = error al establir el UID de l'usuari err_xsessions_dir = error al cercar la carpeta de sessions err_xsessions_open = error al obrir la carpeta de sessions -f1 = F1 aturar -f2 = F2 reiniciar login = iniciar sessió logout = tancar sessió numlock = Bloq Num password = Clau +restart = reiniciar shell = shell +shutdown = aturar wayland = wayland xinitrc = xinitrc diff --git a/res/lang/cs.ini b/res/lang/cs.ini index 8c89f2e..701e0b0 100644 --- a/res/lang/cs.ini +++ b/res/lang/cs.ini @@ -35,12 +35,12 @@ 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í -f1 = F1 vypnout -f2 = F2 restartovat login = uživatel logout = odhlášen numlock = numlock password = heslo +restart = restartovat shell = příkazový řádek +shutdown = vypnout wayland = wayland xinitrc = xinitrc diff --git a/res/lang/de.ini b/res/lang/de.ini index 6977ccd..f062273 100644 --- a/res/lang/de.ini +++ b/res/lang/de.ini @@ -35,12 +35,12 @@ 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 -f1 = F1 Herunterfahren -f2 = F2 Neustarten login = Anmelden logout = Abgemeldet numlock = Numtaste password = Passwort +restart = Neustarten shell = shell +shutdown = Herunterfahren wayland = wayland xinitrc = xinitrc diff --git a/res/lang/en.ini b/res/lang/en.ini index eb497a8..0d7d73d 100644 --- a/res/lang/en.ini +++ b/res/lang/en.ini @@ -35,12 +35,12 @@ err_user_init = failed to initialize user err_user_uid = failed to set user UID err_xsessions_dir = failed to find sessions folder err_xsessions_open = failed to open sessions folder -f1 = F1 shutdown -f2 = F2 reboot login = login logout = logged out numlock = numlock password = password +restart = reboot shell = shell +shutdown = shutdown wayland = wayland xinitrc = xinitrc diff --git a/res/lang/es.ini b/res/lang/es.ini index 899e014..1f963d1 100644 --- a/res/lang/es.ini +++ b/res/lang/es.ini @@ -35,12 +35,12 @@ 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 -f1 = F1 apagar -f2 = F2 reiniciar login = iniciar sesión logout = cerrar sesión numlock = Bloq Num password = contraseña +restart = reiniciar shell = shell +shutdown = apagar wayland = wayland xinitrc = xinitrc diff --git a/res/lang/fr.ini b/res/lang/fr.ini index 3e1a304..daaa27c 100644 --- a/res/lang/fr.ini +++ b/res/lang/fr.ini @@ -35,12 +35,12 @@ err_user_init = échec d'initialisation de l'utilisateur err_user_uid = échec de modification du UID err_xsessions_dir = échec de la recherche du dossier de sessions err_xsessions_open = échec de l'ouverture du dossier de sessions -f1 = F1 éteindre -f2 = F2 redémarrer login = identifiant logout = déconnection numlock = verr.num password = mot de passe +restart = redémarrer shell = shell +shutdown = éteindre wayland = wayland xinitrc = xinitrc diff --git a/res/lang/it.ini b/res/lang/it.ini index 3d82fdd..84a3dea 100644 --- a/res/lang/it.ini +++ b/res/lang/it.ini @@ -35,12 +35,12 @@ 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 -f1 = F1 arresto -f2 = F2 riavvio login = username logout = scollegato numlock = numlock password = password +restart = riavvio shell = shell +shutdown = arresto wayland = wayland xinitrc = xinitrc diff --git a/res/lang/pl.ini b/res/lang/pl.ini index 6b8022f..460ddef 100644 --- a/res/lang/pl.ini +++ b/res/lang/pl.ini @@ -35,12 +35,12 @@ 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 -f1 = F1 wyłącz -f2 = F2 uruchom ponownie login = login logout = wylogowano numlock = numlock password = hasło +restart = uruchom ponownie shell = powłoka +shutdown = wyłącz wayland = wayland xinitrc = xinitrc diff --git a/res/lang/pt.ini b/res/lang/pt.ini index 555d399..a982eb9 100644 --- a/res/lang/pt.ini +++ b/res/lang/pt.ini @@ -35,12 +35,12 @@ 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 -f1 = F1 encerrar -f2 = F2 reiniciar login = iniciar sessão logout = terminar sessão numlock = numlock password = palavra-passe +restart = reiniciar shell = shell +shutdown = encerrar wayland = wayland xinitrc = xinitrc diff --git a/res/lang/pt_BR.ini b/res/lang/pt_BR.ini index cd70c89..68424a7 100644 --- a/res/lang/pt_BR.ini +++ b/res/lang/pt_BR.ini @@ -35,12 +35,12 @@ err_user_init = não foi possível iniciar o usuário err_user_uid = não foi possível definir o UID do usuário err_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 -f1 = F1 desligar -f2 = F2 reiniciar login = conectar logout = desconectado numlock = numlock password = senha +restart = reiniciar shell = shell +shutdown = desligar wayland = wayland xinitrc = xinitrc diff --git a/res/lang/ro.ini b/res/lang/ro.ini index 7e7f18c..8c754d6 100644 --- a/res/lang/ro.ini +++ b/res/lang/ro.ini @@ -35,12 +35,12 @@ err_perm_user = nu s-a putut face downgrade permisiunilor de utilizator -f1 = F1 opreşte sistemul -f2 = F2 resetează login = utilizator logout = opreşte sesiunea numlock = numlock password = parolă +restart = resetează shell = shell +shutdown = opreşte sistemul wayland = wayland xinitrc = xinitrc diff --git a/res/lang/ru.ini b/res/lang/ru.ini index ae915da..9f0b6fc 100644 --- a/res/lang/ru.ini +++ b/res/lang/ru.ini @@ -35,12 +35,12 @@ err_user_init = не удалось инициализировать польз err_user_uid = не удалось установить UID пользователя err_xsessions_dir = не удалось найти сессионную папку err_xsessions_open = не удалось открыть сессионную папку -f1 = F1 выключить -f2 = F2 перезагрузить login = логин logout = logged out numlock = numlock password = пароль +restart = перезагрузить shell = shell +shutdown = выключить wayland = wayland xinitrc = xinitrc diff --git a/res/lang/sr.ini b/res/lang/sr.ini index 1f3618c..59fae5a 100644 --- a/res/lang/sr.ini +++ b/res/lang/sr.ini @@ -35,12 +35,12 @@ 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 -f1 = F1 ugasi -f2 = F2 ponovo pokreni login = korisnik logout = izlogovan numlock = numlock password = lozinka +restart = ponovo pokreni shell = shell +shutdown = ugasi wayland = wayland xinitrc = xinitrc diff --git a/res/lang/sv.ini b/res/lang/sv.ini index 7e98958..2f4505a 100644 --- a/res/lang/sv.ini +++ b/res/lang/sv.ini @@ -35,12 +35,12 @@ 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 -f1 = F1 stäng av -f2 = F2 starta om login = inloggning logout = utloggad numlock = numlock password = lösenord +restart = starta om shell = skal +shutdown = stäng av wayland = wayland xinitrc = xinitrc diff --git a/res/lang/tr.ini b/res/lang/tr.ini index f912d96..885a6db 100644 --- a/res/lang/tr.ini +++ b/res/lang/tr.ini @@ -35,12 +35,12 @@ 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 -f1 = F1 makineyi kapat -f2 = F2 yeniden baslat login = kullanici logout = oturumdan cikis yapildi numlock = numlock password = sifre +restart = yeniden baslat shell = shell +shutdown = makineyi kapat wayland = wayland xinitrc = xinitrc diff --git a/res/lang/uk.ini b/res/lang/uk.ini index 3d5240a..bf52fb6 100644 --- a/res/lang/uk.ini +++ b/res/lang/uk.ini @@ -35,12 +35,12 @@ err_user_init = не вдалося ініціалізувати користу err_user_uid = не вдалося змінити UID користувача err_xsessions_dir = не вдалося знайти каталог сесій err_xsessions_open = не вдалося відкрити каталог сесій -f1 = F1 вимкнути -f2 = F2 перезавантажити login = логін logout = вийти numlock = numlock password = пароль +restart = перезавантажити shell = оболонка +shutdown = вимкнути wayland = wayland xinitrc = xinitrc diff --git a/src/animations/Doom.zig b/src/animations/Doom.zig new file mode 100644 index 0000000..b4d435e --- /dev/null +++ b/src/animations/Doom.zig @@ -0,0 +1,84 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const TerminalBuffer = @import("../tui/TerminalBuffer.zig"); +const utils = @import("../tui/utils.zig"); + +const interop = @import("../interop.zig"); +const termbox = interop.termbox; + +const Doom = @This(); + +pub const STEPS = 13; +pub const FIRE = [_]termbox.tb_cell{ + utils.initCell(' ', 9, 0), + utils.initCell(0x2591, 2, 0), // Red + utils.initCell(0x2592, 2, 0), // Red + utils.initCell(0x2593, 2, 0), // Red + utils.initCell(0x2588, 2, 0), // Red + utils.initCell(0x2591, 4, 2), // Yellow + utils.initCell(0x2592, 4, 2), // Yellow + utils.initCell(0x2593, 4, 2), // Yellow + utils.initCell(0x2588, 4, 2), // Yellow + utils.initCell(0x2591, 8, 4), // White + utils.initCell(0x2592, 8, 4), // White + utils.initCell(0x2593, 8, 4), // White + utils.initCell(0x2588, 8, 4), // White +}; + +allocator: Allocator, +terminal_buffer: *TerminalBuffer, +buffer: []u8, + +pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer) !Doom { + const buffer = try allocator.alloc(u8, terminal_buffer.width * terminal_buffer.height); + initBuffer(buffer, terminal_buffer.width); + + return .{ + .allocator = allocator, + .terminal_buffer = terminal_buffer, + .buffer = buffer, + }; +} + +pub fn deinit(self: Doom) void { + self.allocator.free(self.buffer); +} + +pub fn realloc(self: *Doom) !void { + const buffer = try self.allocator.realloc(self.buffer, self.terminal_buffer.width * self.terminal_buffer.height); + initBuffer(buffer, self.terminal_buffer.width); + self.buffer = buffer; +} + +pub fn draw(self: Doom) void { + for (0..self.terminal_buffer.width) |x| { + for (1..self.terminal_buffer.height) |y| { + const source = y * self.terminal_buffer.width + x; + const random = (self.terminal_buffer.random.int(u16) % 7) & 3; + + var dest = source - random + 1; + if (self.terminal_buffer.width > dest) dest = 0 else dest -= self.terminal_buffer.width; + + const buffer_source = self.buffer[source]; + const buffer_dest_offset = random & 1; + + if (buffer_source < buffer_dest_offset) continue; + + var buffer_dest = buffer_source - buffer_dest_offset; + if (buffer_dest > 12) buffer_dest = 0; + self.buffer[dest] = @intCast(buffer_dest); + + self.terminal_buffer.buffer[dest] = FIRE[buffer_dest]; + self.terminal_buffer.buffer[source] = FIRE[buffer_source]; + } + } +} + +fn initBuffer(buffer: []u8, width: u64) void { + const length = buffer.len - width; + const slice_start = buffer[0..length]; + const slice_end = buffer[length..]; + + @memset(slice_start, 0); + @memset(slice_end, STEPS - 1); +} diff --git a/src/animations/Matrix.zig b/src/animations/Matrix.zig new file mode 100644 index 0000000..7975153 --- /dev/null +++ b/src/animations/Matrix.zig @@ -0,0 +1,189 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const Random = std.rand.Random; +const TerminalBuffer = @import("../tui/TerminalBuffer.zig"); + +const interop = @import("../interop.zig"); +const termbox = interop.termbox; + +pub const FRAME_DELAY: u64 = 8; + +// Allowed codepoints +pub const MIN_CODEPOINT: isize = 33; +pub const MAX_CODEPOINT: isize = 123 - MIN_CODEPOINT; + +// Characters change mid-scroll +pub const MID_SCROLL_CHANGE = true; + +const Matrix = @This(); + +pub const Dot = struct { + value: isize, + is_head: bool, +}; + +pub const Line = struct { + space: isize, + length: isize, + update: isize, +}; + +allocator: Allocator, +terminal_buffer: *TerminalBuffer, +dots: []Dot, +lines: []Line, +frame: u64, +count: u64, + +pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer) !Matrix { + const dots = try allocator.alloc(Dot, terminal_buffer.width * (terminal_buffer.height + 1)); + const lines = try allocator.alloc(Line, terminal_buffer.width); + + initBuffers(dots, lines, terminal_buffer.width, terminal_buffer.height, terminal_buffer.random); + + return .{ + .allocator = allocator, + .terminal_buffer = terminal_buffer, + .dots = dots, + .lines = lines, + .frame = 3, + .count = 0, + }; +} + +pub fn deinit(self: Matrix) void { + self.allocator.free(self.dots); + self.allocator.free(self.lines); +} + +pub fn realloc(self: *Matrix) !void { + const dots = try self.allocator.realloc(self.dots, self.terminal_buffer.width * (self.terminal_buffer.height + 1)); + const lines = try self.allocator.realloc(self.lines, self.terminal_buffer.width); + + initBuffers(dots, lines, self.terminal_buffer.width, self.terminal_buffer.height, self.terminal_buffer.random); + + self.dots = dots; + self.lines = lines; +} + +// TODO: Fix!! +pub fn draw(self: *Matrix) void { + var first_column = false; + + self.count += 1; + if (self.count > FRAME_DELAY) { + self.frame += 1; + if (self.frame > 4) self.frame = 1; + self.count = 0; + + var x: u64 = 0; + while (x < self.terminal_buffer.width) : (x += 2) { + var line = self.lines[x]; + if (self.frame <= line.update) continue; + + var tail: u64 = 0; + if (self.dots[x].value == -1 and self.dots[self.terminal_buffer.width + x].value == ' ') { + if (line.space <= 0) { + const random = self.terminal_buffer.random.int(i16); + const h: isize = @intCast(self.terminal_buffer.height); + line.length = @mod(random, h - 3) + 3; + line.space = @mod(random, h) + 1; + self.dots[x].value = @mod(random, MAX_CODEPOINT) + MIN_CODEPOINT; + } else { + line.space -= 1; + } + + self.lines[x] = line; + first_column = true; + + var y: u64 = 0; + var seg_length: u64 = 0; + + while (y <= self.terminal_buffer.height) : (y += 1) { + // TODO: Are all these y/height checks required? + var dot = self.dots[y * self.terminal_buffer.width + x]; + + // Skip over spaces + while (dot.value == ' ' or dot.value == -1) { + y += 1; + if (y > self.terminal_buffer.height) break; + + dot = self.dots[y * self.terminal_buffer.width + x]; + } + if (y > self.terminal_buffer.height) break; + + // Find the head of this column + tail = y; + seg_length = 0; + while (y <= self.terminal_buffer.height and dot.value != ' ' and dot.value != -1) { + dot.is_head = false; + if (MID_SCROLL_CHANGE) { + const random = self.terminal_buffer.random.int(i16); + if (@mod(random, 8) == 0) dot.value = @mod(random, MAX_CODEPOINT) + MIN_CODEPOINT; + } + self.dots[y * self.terminal_buffer.width + x] = dot; + + y += 1; + seg_length += 1; + dot = self.dots[y * self.terminal_buffer.width + x]; + } + + // The head is down offscreen + if (y > self.terminal_buffer.height) { + self.dots[tail * self.terminal_buffer.width + x].value = ' '; + continue; // TODO: Shouldn't this be break? + } + + const random = self.terminal_buffer.random.int(i16); + self.dots[y * self.terminal_buffer.width + x].value = @mod(random, MAX_CODEPOINT) + MIN_CODEPOINT; + self.dots[y * self.terminal_buffer.width + x].is_head = true; + + if (seg_length > line.length or !first_column) { + self.dots[tail * self.terminal_buffer.width + x].value = ' '; + self.dots[x].value = -1; + } + first_column = false; + } + } + } + } + + var x: u64 = 0; + while (x < self.terminal_buffer.width) : (x += 2) { + var y: u64 = 1; + while (y <= self.terminal_buffer.height) : (y += 1) { + const dot = self.dots[y * self.terminal_buffer.width + x]; + var fg: u32 = @intCast(termbox.TB_GREEN); + + if (dot.value == -1 or dot.value == ' ') { + termbox.tb_change_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_change_cell(@intCast(x), @intCast(y - 1), @intCast(dot.value), fg, termbox.TB_DEFAULT); + } + } +} + +fn initBuffers(dots: []Dot, lines: []Line, width: u64, height: u64, random: Random) void { + var y: u64 = 0; + while (y <= height) : (y += 1) { + var x: u64 = 0; + while (x < width) : (x += 2) { + dots[y * width + x].value = -1; + } + } + + var x: u64 = 0; + while (x < width) : (x += 2) { + var line = lines[x]; + const h: isize = @intCast(height); + line.space = @mod(random.int(i16), h) + 1; + line.length = @mod(random.int(i16), h - 3) + 3; + line.update = @mod(random.int(i16), 3) + 1; + lines[x] = line; + + dots[width + x].value = ' '; + } +} diff --git a/src/auth.zig b/src/auth.zig new file mode 100644 index 0000000..0c60e3b --- /dev/null +++ b/src/auth.zig @@ -0,0 +1,65 @@ +const std = @import("std"); +const enums = @import("enums.zig"); +const interop = @import("interop.zig"); +const TerminalBuffer = @import("tui/TerminalBuffer.zig"); +const Desktop = @import("tui/components/Desktop.zig"); +const Text = @import("tui/components/Text.zig"); +const Allocator = std.mem.Allocator; + +// TODO +pub fn authenticate(allocator: Allocator, tty: u8, buffer: TerminalBuffer, desktop: Desktop, login: Text, password: Text) !void { + _ = buffer; + + const uid = interop.getuid(); + + var tty_buffer = std.mem.zeroes([@sizeOf(u8) + 1]u8); + var uid_buffer = std.mem.zeroes([10 + @sizeOf(u32) + 1]u8); + + const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{tty}); + const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid}); + const current_environment = desktop.environments.items[desktop.current]; + + // Add XDG environment variables + setXdgSessionEnv(current_environment.display_server); + try setXdgEnv(allocator, tty_str, uid_str, current_environment.xdg_name); + + // Open the PAM session + var credentials = [_][]const u8{ login.text.items, password.text.items }; + const conv = interop.pam.pam_conv{ + .conv = loginConv, + .appdata_ptr = @ptrCast(&credentials), + }; + _ = conv; +} + +fn setXdgSessionEnv(display_server: enums.DisplayServer) void { + _ = interop.setenv("XDG_SESSION_TYPE", switch (display_server) { + .wayland => "wayland", + .shell => "tty", + .xinitrc, .x11 => "x11", + }, 0); +} + +fn setXdgEnv(allocator: Allocator, tty_str: [:0]u8, uid_str: [:0]u8, desktop_name: []const u8) !void { + const desktop_name_z = try allocator.dupeZ(u8, desktop_name); + defer allocator.free(desktop_name_z); + + _ = interop.setenv("XDG_RUNTIME_DIR", uid_str, 0); + _ = interop.setenv("XDG_SESSION_CLASS", "user", 0); + _ = interop.setenv("XDG_SESSION_ID", "1", 0); + _ = interop.setenv("XDG_SESSION_DESKTOP", desktop_name_z, 0); + _ = interop.setenv("XDG_SEAT", "seat0", 0); + _ = interop.setenv("XDG_VTNR", tty_str, 0); +} + +fn loginConv( + num_msg: c_int, + msg: [*][*]const interop.pam.pam_message, + resp: [*][*]const interop.pam.pam_response, + appdata_ptr: ?*anyopaque, +) c_int { + _ = num_msg; + _ = msg; + _ = resp; + _ = appdata_ptr; +} diff --git a/src/bigclock.h b/src/bigclock.h deleted file mode 100644 index 2de834a..0000000 --- a/src/bigclock.h +++ /dev/null @@ -1,146 +0,0 @@ -#include - -#define CLOCK_W 5 -#define CLOCK_H 5 - -#if defined(__linux__) || defined(__FreeBSD__) - #define X 0x2593 - #define _ 0x0000 -#else - #define X '#' - #define _ 0 -#endif - -#if CLOCK_W == 5 && CLOCK_H == 5 - -uint32_t CLOCK_0[] = { - X,X,X,X,X, - X,X,_,X,X, - X,X,_,X,X, - X,X,_,X,X, - X,X,X,X,X -}; - -uint32_t CLOCK_1[] = { - _,_,_,X,X, - _,_,_,X,X, - _,_,_,X,X, - _,_,_,X,X, - _,_,_,X,X -}; - -uint32_t CLOCK_2[] = { - X,X,X,X,X, - _,_,_,X,X, - X,X,X,X,X, - X,X,_,_,_, - X,X,X,X,X -}; - -uint32_t CLOCK_3[] = { - X,X,X,X,X, - _,_,_,X,X, - X,X,X,X,X, - _,_,_,X,X, - X,X,X,X,X -}; - -uint32_t CLOCK_4[] = { - X,X,_,X,X, - X,X,_,X,X, - X,X,X,X,X, - _,_,_,X,X, - _,_,_,X,X -}; - -uint32_t CLOCK_5[] = { - X,X,X,X,X, - X,X,_,_,_, - X,X,X,X,X, - _,_,_,X,X, - X,X,X,X,X -}; - -uint32_t CLOCK_6[] = { - X,X,X,X,X, - X,X,_,_,_, - X,X,X,X,X, - X,X,_,X,X, - X,X,X,X,X, -}; - -uint32_t CLOCK_7[] = { - X,X,X,X,X, - _,_,_,X,X, - _,_,_,X,X, - _,_,_,X,X, - _,_,_,X,X -}; - -uint32_t CLOCK_8[] = { - X,X,X,X,X, - X,X,_,X,X, - X,X,X,X,X, - X,X,_,X,X, - X,X,X,X,X -}; - -uint32_t CLOCK_9[] = { - X,X,X,X,X, - X,X,_,X,X, - X,X,X,X,X, - _,_,_,X,X, - X,X,X,X,X -}; - -uint32_t CLOCK_S[] = { - _,_,_,_,_, - _,_,X,_,_, - _,_,_,_,_, - _,_,X,_,_, - _,_,_,_,_ -}; - -uint32_t CLOCK_E[] = { - _,_,_,_,_, - _,_,_,_,_, - _,_,_,_,_, - _,_,_,_,_, - _,_,_,_,_ -}; - -#endif - -#undef X -#undef _ - -static inline uint32_t* CLOCK_N(char c) -{ - switch(c) - { - case '0': - return CLOCK_0; - case '1': - return CLOCK_1; - case '2': - return CLOCK_2; - case '3': - return CLOCK_3; - case '4': - return CLOCK_4; - case '5': - return CLOCK_5; - case '6': - return CLOCK_6; - case '7': - return CLOCK_7; - case '8': - return CLOCK_8; - case '9': - return CLOCK_9; - case ':': - return CLOCK_S; - default: - return CLOCK_E; - } -} diff --git a/src/bigclock.zig b/src/bigclock.zig new file mode 100644 index 0000000..0150dc2 --- /dev/null +++ b/src/bigclock.zig @@ -0,0 +1,140 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const interop = @import("interop.zig"); +const utils = @import("tui/utils.zig"); + +const termbox = interop.termbox; + +const X: u32 = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) 0x2593 else '#'; +const O: u32 = 0; + +pub const WIDTH: u64 = 5; +pub const HEIGHT: u64 = 5; +pub const SIZE = WIDTH * HEIGHT; + +// zig fmt: off +const ZERO = [_]u32{ + X,X,X,X,X, + X,X,O,X,X, + X,X,O,X,X, + X,X,O,X,X, + X,X,X,X,X, +}; +const ONE = [_]u32{ + O,O,O,X,X, + O,O,O,X,X, + O,O,O,X,X, + O,O,O,X,X, + O,O,O,X,X, +}; +const TWO = [_]u32{ + X,X,X,X,X, + O,O,O,X,X, + X,X,X,X,X, + X,X,O,O,O, + X,X,X,X,X, +}; +const THREE = [_]u32{ + X,X,X,X,X, + O,O,O,X,X, + X,X,X,X,X, + O,O,O,X,X, + X,X,X,X,X, +}; +const FOUR = [_]u32{ + X,X,O,X,X, + X,X,O,X,X, + X,X,X,X,X, + O,O,O,X,X, + O,O,O,X,X, +}; +const FIVE = [_]u32{ + X,X,X,X,X, + X,X,O,O,O, + X,X,X,X,X, + O,O,O,X,X, + X,X,X,X,X, +}; +const SIX = [_]u32{ + X,X,X,X,X, + X,X,O,O,O, + X,X,X,X,X, + X,X,O,X,X, + X,X,X,X,X, +}; +const SEVEN = [_]u32{ + X,X,X,X,X, + O,O,O,X,X, + O,O,O,X,X, + O,O,O,X,X, + O,O,O,X,X, +}; +const EIGHT = [_]u32{ + X,X,X,X,X, + X,X,O,X,X, + X,X,X,X,X, + X,X,O,X,X, + X,X,X,X,X, +}; +const NINE = [_]u32{ + X,X,X,X,X, + X,X,O,X,X, + X,X,X,X,X, + O,O,O,X,X, + X,X,X,X,X, +}; +const S = [_]u32{ + O,O,O,O,O, + O,O,X,O,O, + O,O,O,O,O, + O,O,X,O,O, + O,O,O,O,O, +}; +const E = [_]u32{ + O,O,O,O,O, + O,O,O,O,O, + O,O,O,O,O, + O,O,O,O,O, + O,O,O,O,O, +}; +// zig fmt: on + +pub fn clockCell(animate: bool, char: u8, fg: u8, bg: u8) [SIZE]termbox.tb_cell { + var cells = std.mem.zeroes([SIZE]termbox.tb_cell); + + var tv = std.mem.zeroes(std.c.timeval); + _ = std.c.gettimeofday(&tv, null); + + const clock_chars = toBigNumber(if (animate and char == ':' and @divTrunc(tv.tv_usec, 500000) != 0) ' ' else char); + for (0..cells.len) |i| cells[i] = utils.initCell(clock_chars[i], fg, bg); + + return cells; +} + +pub fn alphaBlit(buffer: [*c]termbox.tb_cell, x: u64, y: u64, tb_width: u64, tb_height: u64, cells: [SIZE]termbox.tb_cell) void { + if (x + WIDTH >= tb_width or y + HEIGHT >= tb_height) return; + + for (0..HEIGHT) |yy| { + for (0..WIDTH) |xx| { + const cell = cells[yy * WIDTH + xx]; + if (cell.ch != 0) buffer[(y + yy) * tb_width + (x + xx)] = cell; + } + } +} + +fn toBigNumber(char: u8) []const u32 { + return switch (char) { + '0' => &ZERO, + '1' => &ONE, + '2' => &TWO, + '3' => &THREE, + '4' => &FOUR, + '5' => &FIVE, + '6' => &SIX, + '7' => &SEVEN, + '8' => &EIGHT, + '9' => &NINE, + ':' => &S, + else => &E, + }; +} diff --git a/src/config.h b/src/config.h deleted file mode 100644 index b5196e6..0000000 --- a/src/config.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef H_LY_CONFIG -#define H_LY_CONFIG - -#include -#include - -struct lang -{ - char* capslock; - char* err_alloc; - char* err_bounds; - char* err_chdir; - char* err_console_dev; - char* err_dgn_oob; - char* err_domain; - char* err_hostname; - char* err_mlock; - char* err_null; - char* err_pam; - char* err_pam_abort; - char* err_pam_acct_expired; - char* err_pam_auth; - char* err_pam_authinfo_unavail; - char* err_pam_authok_reqd; - char* err_pam_buf; - char* err_pam_cred_err; - char* err_pam_cred_expired; - char* err_pam_cred_insufficient; - char* err_pam_cred_unavail; - char* err_pam_maxtries; - char* err_pam_perm_denied; - char* err_pam_session; - char* err_pam_sys; - char* err_pam_user_unknown; - char* err_path; - char* err_perm_dir; - char* err_perm_group; - char* err_perm_user; - char* err_pwnam; - char* err_user_gid; - char* err_user_init; - char* err_user_uid; - char* err_xsessions_dir; - char* err_xsessions_open; - char* f1; - char* f2; - char* login; - char* logout; - char* numlock; - char* password; - char* shell; - char* wayland; - char* xinitrc; -}; - -struct config -{ - bool animate; - uint8_t animation; - char asterisk; - uint8_t bg; - bool bigclock; - bool blank_box; - bool blank_password; - char* clock; - char* console_dev; - uint8_t default_input; - uint8_t fg; - bool hide_borders; - bool hide_f1_commands; - uint8_t input_len; - char* lang; - bool load; - uint8_t margin_box_h; - uint8_t margin_box_v; - uint8_t max_desktop_len; - uint8_t max_login_len; - uint8_t max_password_len; - char* mcookie_cmd; - uint16_t min_refresh_delta; - char* path; - char* restart_cmd; - bool save; - char* save_file; - char* service_name; - char* shutdown_cmd; - char* term_reset_cmd; - uint8_t tty; - char* wayland_cmd; - bool wayland_specifier; - char* waylandsessions; - char* x_cmd; - char* xinitrc; - char* x_cmd_setup; - char* xauth_cmd; - char* xsessions; -}; - -extern struct lang lang; -extern struct config config; - -#endif diff --git a/src/config.zig b/src/config.zig deleted file mode 100644 index 5ea6f0b..0000000 --- a/src/config.zig +++ /dev/null @@ -1,424 +0,0 @@ -const std = @import("std"); -const ini = @import("ini"); -const main = @import("main.zig"); -const interop = @import("interop.zig"); - -const INI_CONFIG_PATH: []const u8 = "/etc/ly/"; -const INI_CONFIG_MAX_SIZE: usize = 16 * 1024; - -pub const Inputs = enum { - session, - login, - password, -}; - -pub const LyConfig = struct { - ly: struct { - animate: bool, - animation: u8, - asterisk: u8, - bg: u8, - bigclock: bool, - blank_box: bool, - blank_password: bool, - clock: []const u8, - console_dev: []const u8, - default_input: Inputs, - fg: u8, - hide_borders: bool, - hide_f1_commands: bool, - input_len: u8, - lang: []const u8, - load: bool, - margin_box_h: u8, - margin_box_v: u8, - max_desktop_len: u8, - max_login_len: u8, - max_password_len: u8, - mcookie_cmd: []const u8, - min_refresh_delta: u16, - path: []const u8, - restart_cmd: []const u8, - save: bool, - save_file: []const u8, - service_name: []const u8, - shutdown_cmd: []const u8, - term_reset_cmd: []const u8, - tty: u8, - wayland_cmd: []const u8, - wayland_specifier: bool, - waylandsessions: []const u8, - x_cmd: []const u8, - xinitrc: []const u8, - x_cmd_setup: []const u8, - xauth_cmd: []const u8, - xsessions: []const u8, - }, -}; - -pub const LyLang = struct { - ly: struct { - capslock: []const u8, - err_alloc: []const u8, - err_bounds: []const u8, - err_chdir: []const u8, - err_console_dev: []const u8, - err_dgn_oob: []const u8, - err_domain: []const u8, - err_hostname: []const u8, - err_mlock: []const u8, - err_null: []const u8, - err_pam: []const u8, - err_pam_abort: []const u8, - err_pam_acct_expired: []const u8, - err_pam_auth: []const u8, - err_pam_authinfo_unavail: []const u8, - err_pam_authok_reqd: []const u8, - err_pam_buf: []const u8, - err_pam_cred_err: []const u8, - err_pam_cred_expired: []const u8, - err_pam_cred_insufficient: []const u8, - err_pam_cred_unavail: []const u8, - err_pam_maxtries: []const u8, - err_pam_perm_denied: []const u8, - err_pam_session: []const u8, - err_pam_sys: []const u8, - err_pam_user_unknown: []const u8, - err_path: []const u8, - err_perm_dir: []const u8, - err_perm_group: []const u8, - err_perm_user: []const u8, - err_pwnam: []const u8, - err_user_gid: []const u8, - err_user_init: []const u8, - err_user_uid: []const u8, - err_xsessions_dir: []const u8, - err_xsessions_open: []const u8, - f1: []const u8, - f2: []const u8, - login: []const u8, - logout: []const u8, - numlock: []const u8, - password: []const u8, - shell: []const u8, - wayland: []const u8, - xinitrc: []const u8, - }, -}; - -pub var ly_config: LyConfig = undefined; -pub var ly_lang: LyLang = undefined; - -var config_buffer: []u8 = undefined; -var lang_buffer: []u8 = undefined; - -var config_clock: [:0]u8 = undefined; -var config_console_dev: [:0]u8 = undefined; -var config_lang: [:0]u8 = undefined; -var config_mcookie_cmd: [:0]u8 = undefined; -var config_path: [:0]u8 = undefined; -var config_restart_cmd: [:0]u8 = undefined; -var config_save_file: [:0]u8 = undefined; -var config_service_name: [:0]u8 = undefined; -var config_shutdown_cmd: [:0]u8 = undefined; -var config_term_reset_cmd: [:0]u8 = undefined; -var config_wayland_cmd: [:0]u8 = undefined; -var config_waylandsessions: [:0]u8 = undefined; -var config_x_cmd: [:0]u8 = undefined; -var config_xinitrc: [:0]u8 = undefined; -var config_x_cmd_setup: [:0]u8 = undefined; -var config_xauth_cmd: [:0]u8 = undefined; -var config_xsessions: [:0]u8 = undefined; - -var lang_capslock: [:0]u8 = undefined; -var lang_err_alloc: [:0]u8 = undefined; -var lang_err_bounds: [:0]u8 = undefined; -var lang_err_chdir: [:0]u8 = undefined; -var lang_err_console_dev: [:0]u8 = undefined; -var lang_err_dgn_oob: [:0]u8 = undefined; -var lang_err_domain: [:0]u8 = undefined; -var lang_err_hostname: [:0]u8 = undefined; -var lang_err_mlock: [:0]u8 = undefined; -var lang_err_null: [:0]u8 = undefined; -var lang_err_pam: [:0]u8 = undefined; -var lang_err_pam_abort: [:0]u8 = undefined; -var lang_err_pam_acct_expired: [:0]u8 = undefined; -var lang_err_pam_auth: [:0]u8 = undefined; -var lang_err_pam_authinfo_unavail: [:0]u8 = undefined; -var lang_err_pam_authok_reqd: [:0]u8 = undefined; -var lang_err_pam_buf: [:0]u8 = undefined; -var lang_err_pam_cred_err: [:0]u8 = undefined; -var lang_err_pam_cred_expired: [:0]u8 = undefined; -var lang_err_pam_cred_insufficient: [:0]u8 = undefined; -var lang_err_pam_cred_unavail: [:0]u8 = undefined; -var lang_err_pam_maxtries: [:0]u8 = undefined; -var lang_err_pam_perm_denied: [:0]u8 = undefined; -var lang_err_pam_session: [:0]u8 = undefined; -var lang_err_pam_sys: [:0]u8 = undefined; -var lang_err_pam_user_unknown: [:0]u8 = undefined; -var lang_err_path: [:0]u8 = undefined; -var lang_err_perm_dir: [:0]u8 = undefined; -var lang_err_perm_group: [:0]u8 = undefined; -var lang_err_perm_user: [:0]u8 = undefined; -var lang_err_pwnam: [:0]u8 = undefined; -var lang_err_user_gid: [:0]u8 = undefined; -var lang_err_user_init: [:0]u8 = undefined; -var lang_err_user_uid: [:0]u8 = undefined; -var lang_err_xsessions_dir: [:0]u8 = undefined; -var lang_err_xsessions_open: [:0]u8 = undefined; -var lang_f1: [:0]u8 = undefined; -var lang_f2: [:0]u8 = undefined; -var lang_login: [:0]u8 = undefined; -var lang_logout: [:0]u8 = undefined; -var lang_numlock: [:0]u8 = undefined; -var lang_password: [:0]u8 = undefined; -var lang_shell: [:0]u8 = undefined; -var lang_wayland: [:0]u8 = undefined; -var lang_xinitrc: [:0]u8 = undefined; - -pub fn config_load(cfg_path: []const u8) !void { - var file = try std.fs.cwd().openFile(if (cfg_path.len == 0) INI_CONFIG_PATH ++ "config.ini" else cfg_path, .{}); - defer file.close(); - - config_buffer = try main.allocator.alloc(u8, INI_CONFIG_MAX_SIZE); - - var length = try file.readAll(config_buffer); - - ly_config = try ini.readToStruct(LyConfig, config_buffer[0..length]); - - config_clock = try interop.c_str(ly_config.ly.clock); - config_console_dev = try interop.c_str(ly_config.ly.console_dev); - config_lang = try interop.c_str(ly_config.ly.lang); - config_mcookie_cmd = try interop.c_str(ly_config.ly.mcookie_cmd); - config_path = try interop.c_str(ly_config.ly.path); - config_restart_cmd = try interop.c_str(ly_config.ly.restart_cmd); - config_save_file = try interop.c_str(ly_config.ly.save_file); - config_service_name = try interop.c_str(ly_config.ly.service_name); - config_shutdown_cmd = try interop.c_str(ly_config.ly.shutdown_cmd); - config_term_reset_cmd = try interop.c_str(ly_config.ly.term_reset_cmd); - config_wayland_cmd = try interop.c_str(ly_config.ly.wayland_cmd); - config_waylandsessions = try interop.c_str(ly_config.ly.waylandsessions); - config_x_cmd = try interop.c_str(ly_config.ly.x_cmd); - config_xinitrc = try interop.c_str(ly_config.ly.xinitrc); - config_x_cmd_setup = try interop.c_str(ly_config.ly.x_cmd_setup); - config_xauth_cmd = try interop.c_str(ly_config.ly.xauth_cmd); - config_xsessions = try interop.c_str(ly_config.ly.xsessions); - - main.c_config.animate = ly_config.ly.animate; - main.c_config.animation = ly_config.ly.animation; - main.c_config.asterisk = ly_config.ly.asterisk; - main.c_config.bg = ly_config.ly.bg; - main.c_config.bigclock = ly_config.ly.bigclock; - main.c_config.blank_box = ly_config.ly.blank_box; - main.c_config.blank_password = ly_config.ly.blank_password; - main.c_config.clock = config_clock.ptr; - main.c_config.console_dev = config_console_dev.ptr; - main.c_config.default_input = @intFromEnum(ly_config.ly.default_input); - main.c_config.fg = ly_config.ly.fg; - main.c_config.hide_borders = ly_config.ly.hide_borders; - main.c_config.hide_f1_commands = ly_config.ly.hide_f1_commands; - main.c_config.input_len = ly_config.ly.input_len; - main.c_config.lang = config_lang.ptr; - main.c_config.load = ly_config.ly.load; - main.c_config.margin_box_h = ly_config.ly.margin_box_h; - main.c_config.margin_box_v = ly_config.ly.margin_box_v; - main.c_config.max_desktop_len = ly_config.ly.max_desktop_len; - main.c_config.max_login_len = ly_config.ly.max_login_len; - main.c_config.max_password_len = ly_config.ly.max_password_len; - main.c_config.mcookie_cmd = config_mcookie_cmd.ptr; - main.c_config.min_refresh_delta = ly_config.ly.min_refresh_delta; - main.c_config.path = config_path.ptr; - main.c_config.restart_cmd = config_restart_cmd.ptr; - main.c_config.save = ly_config.ly.save; - main.c_config.save_file = config_save_file.ptr; - main.c_config.service_name = config_service_name.ptr; - main.c_config.shutdown_cmd = config_shutdown_cmd.ptr; - main.c_config.term_reset_cmd = config_term_reset_cmd.ptr; - main.c_config.tty = ly_config.ly.tty; - main.c_config.wayland_cmd = config_wayland_cmd.ptr; - main.c_config.wayland_specifier = ly_config.ly.wayland_specifier; - main.c_config.waylandsessions = config_waylandsessions.ptr; - main.c_config.x_cmd = config_x_cmd.ptr; - main.c_config.xinitrc = config_xinitrc.ptr; - main.c_config.x_cmd_setup = config_x_cmd_setup.ptr; - main.c_config.xauth_cmd = config_xauth_cmd.ptr; - main.c_config.xsessions = config_xsessions.ptr; -} - -pub fn lang_load() !void { - var path = try std.fmt.allocPrint(main.allocator, "{s}{s}.ini", .{ INI_CONFIG_PATH ++ "lang/", ly_config.ly.lang }); - defer main.allocator.free(path); - - var file = try std.fs.cwd().openFile(path, .{}); - defer file.close(); - - lang_buffer = try main.allocator.alloc(u8, INI_CONFIG_MAX_SIZE); - - var length = try file.readAll(lang_buffer); - - ly_lang = try ini.readToStruct(LyLang, lang_buffer[0..length]); - - lang_capslock = try interop.c_str(ly_lang.ly.capslock); - lang_err_alloc = try interop.c_str(ly_lang.ly.err_alloc); - lang_err_bounds = try interop.c_str(ly_lang.ly.err_bounds); - lang_err_chdir = try interop.c_str(ly_lang.ly.err_chdir); - lang_err_console_dev = try interop.c_str(ly_lang.ly.err_console_dev); - lang_err_dgn_oob = try interop.c_str(ly_lang.ly.err_dgn_oob); - lang_err_domain = try interop.c_str(ly_lang.ly.err_domain); - lang_err_hostname = try interop.c_str(ly_lang.ly.err_hostname); - lang_err_mlock = try interop.c_str(ly_lang.ly.err_mlock); - lang_err_null = try interop.c_str(ly_lang.ly.err_null); - lang_err_pam = try interop.c_str(ly_lang.ly.err_pam); - lang_err_pam_abort = try interop.c_str(ly_lang.ly.err_pam_abort); - lang_err_pam_acct_expired = try interop.c_str(ly_lang.ly.err_pam_acct_expired); - lang_err_pam_auth = try interop.c_str(ly_lang.ly.err_pam_auth); - lang_err_pam_authinfo_unavail = try interop.c_str(ly_lang.ly.err_pam_authinfo_unavail); - lang_err_pam_authok_reqd = try interop.c_str(ly_lang.ly.err_pam_authok_reqd); - lang_err_pam_buf = try interop.c_str(ly_lang.ly.err_pam_buf); - lang_err_pam_cred_err = try interop.c_str(ly_lang.ly.err_pam_cred_err); - lang_err_pam_cred_expired = try interop.c_str(ly_lang.ly.err_pam_cred_expired); - lang_err_pam_cred_insufficient = try interop.c_str(ly_lang.ly.err_pam_cred_insufficient); - lang_err_pam_cred_unavail = try interop.c_str(ly_lang.ly.err_pam_cred_unavail); - lang_err_pam_maxtries = try interop.c_str(ly_lang.ly.err_pam_maxtries); - lang_err_pam_perm_denied = try interop.c_str(ly_lang.ly.err_pam_perm_denied); - lang_err_pam_session = try interop.c_str(ly_lang.ly.err_pam_session); - lang_err_pam_sys = try interop.c_str(ly_lang.ly.err_pam_sys); - lang_err_pam_user_unknown = try interop.c_str(ly_lang.ly.err_pam_user_unknown); - lang_err_path = try interop.c_str(ly_lang.ly.err_path); - lang_err_perm_dir = try interop.c_str(ly_lang.ly.err_perm_dir); - lang_err_perm_group = try interop.c_str(ly_lang.ly.err_perm_group); - lang_err_perm_user = try interop.c_str(ly_lang.ly.err_perm_user); - lang_err_pwnam = try interop.c_str(ly_lang.ly.err_pwnam); - lang_err_user_gid = try interop.c_str(ly_lang.ly.err_user_gid); - lang_err_user_init = try interop.c_str(ly_lang.ly.err_user_init); - lang_err_user_uid = try interop.c_str(ly_lang.ly.err_user_uid); - lang_err_xsessions_dir = try interop.c_str(ly_lang.ly.err_xsessions_dir); - lang_err_xsessions_open = try interop.c_str(ly_lang.ly.err_xsessions_open); - lang_f1 = try interop.c_str(ly_lang.ly.f1); - lang_f2 = try interop.c_str(ly_lang.ly.f2); - lang_login = try interop.c_str(ly_lang.ly.login); - lang_logout = try interop.c_str(ly_lang.ly.logout); - lang_numlock = try interop.c_str(ly_lang.ly.numlock); - lang_password = try interop.c_str(ly_lang.ly.password); - lang_shell = try interop.c_str(ly_lang.ly.shell); - lang_wayland = try interop.c_str(ly_lang.ly.wayland); - lang_xinitrc = try interop.c_str(ly_lang.ly.xinitrc); - - main.c_lang.capslock = lang_capslock.ptr; - main.c_lang.err_alloc = lang_err_alloc.ptr; - main.c_lang.err_bounds = lang_err_bounds.ptr; - main.c_lang.err_chdir = lang_err_chdir.ptr; - main.c_lang.err_console_dev = lang_err_console_dev.ptr; - main.c_lang.err_dgn_oob = lang_err_dgn_oob.ptr; - main.c_lang.err_domain = lang_err_domain.ptr; - main.c_lang.err_hostname = lang_err_hostname.ptr; - main.c_lang.err_mlock = lang_err_mlock.ptr; - main.c_lang.err_null = lang_err_null.ptr; - main.c_lang.err_pam = lang_err_pam.ptr; - main.c_lang.err_pam_abort = lang_err_pam_abort.ptr; - main.c_lang.err_pam_acct_expired = lang_err_pam_acct_expired.ptr; - main.c_lang.err_pam_auth = lang_err_pam_auth.ptr; - main.c_lang.err_pam_authinfo_unavail = lang_err_pam_authinfo_unavail.ptr; - main.c_lang.err_pam_authok_reqd = lang_err_pam_authok_reqd.ptr; - main.c_lang.err_pam_buf = lang_err_pam_buf.ptr; - main.c_lang.err_pam_cred_err = lang_err_pam_cred_err.ptr; - main.c_lang.err_pam_cred_expired = lang_err_pam_cred_expired.ptr; - main.c_lang.err_pam_cred_insufficient = lang_err_pam_cred_insufficient.ptr; - main.c_lang.err_pam_cred_unavail = lang_err_pam_cred_unavail.ptr; - main.c_lang.err_pam_maxtries = lang_err_pam_maxtries.ptr; - main.c_lang.err_pam_perm_denied = lang_err_pam_perm_denied.ptr; - main.c_lang.err_pam_session = lang_err_pam_session.ptr; - main.c_lang.err_pam_sys = lang_err_pam_sys.ptr; - main.c_lang.err_pam_user_unknown = lang_err_pam_user_unknown.ptr; - main.c_lang.err_path = lang_err_path.ptr; - main.c_lang.err_perm_dir = lang_err_perm_dir.ptr; - main.c_lang.err_perm_group = lang_err_perm_group.ptr; - main.c_lang.err_perm_user = lang_err_perm_user.ptr; - main.c_lang.err_pwnam = lang_err_pwnam.ptr; - main.c_lang.err_user_gid = lang_err_user_gid.ptr; - main.c_lang.err_user_init = lang_err_user_init.ptr; - main.c_lang.err_user_uid = lang_err_user_uid.ptr; - main.c_lang.err_xsessions_dir = lang_err_xsessions_dir.ptr; - main.c_lang.err_xsessions_open = lang_err_xsessions_open.ptr; - main.c_lang.f1 = lang_f1.ptr; - main.c_lang.f2 = lang_f2.ptr; - main.c_lang.login = lang_login.ptr; - main.c_lang.logout = lang_logout.ptr; - main.c_lang.numlock = lang_numlock.ptr; - main.c_lang.password = lang_password.ptr; - main.c_lang.shell = lang_shell.ptr; - main.c_lang.wayland = lang_wayland.ptr; - main.c_lang.xinitrc = lang_xinitrc.ptr; -} - -pub fn config_free() void { - interop.allocator.free(config_clock); - interop.allocator.free(config_console_dev); - interop.allocator.free(config_lang); - interop.allocator.free(config_mcookie_cmd); - interop.allocator.free(config_path); - interop.allocator.free(config_restart_cmd); - interop.allocator.free(config_save_file); - interop.allocator.free(config_service_name); - interop.allocator.free(config_shutdown_cmd); - interop.allocator.free(config_term_reset_cmd); - interop.allocator.free(config_wayland_cmd); - interop.allocator.free(config_waylandsessions); - interop.allocator.free(config_x_cmd); - interop.allocator.free(config_xinitrc); - interop.allocator.free(config_x_cmd_setup); - interop.allocator.free(config_xauth_cmd); - interop.allocator.free(config_xsessions); - - main.allocator.free(config_buffer); -} - -pub fn lang_free() void { - interop.allocator.free(lang_capslock); - interop.allocator.free(lang_err_alloc); - interop.allocator.free(lang_err_bounds); - interop.allocator.free(lang_err_chdir); - interop.allocator.free(lang_err_console_dev); - interop.allocator.free(lang_err_dgn_oob); - interop.allocator.free(lang_err_domain); - interop.allocator.free(lang_err_hostname); - interop.allocator.free(lang_err_mlock); - interop.allocator.free(lang_err_null); - interop.allocator.free(lang_err_pam); - interop.allocator.free(lang_err_pam_abort); - interop.allocator.free(lang_err_pam_acct_expired); - interop.allocator.free(lang_err_pam_auth); - interop.allocator.free(lang_err_pam_authinfo_unavail); - interop.allocator.free(lang_err_pam_authok_reqd); - interop.allocator.free(lang_err_pam_buf); - interop.allocator.free(lang_err_pam_cred_err); - interop.allocator.free(lang_err_pam_cred_expired); - interop.allocator.free(lang_err_pam_cred_insufficient); - interop.allocator.free(lang_err_pam_cred_unavail); - interop.allocator.free(lang_err_pam_maxtries); - interop.allocator.free(lang_err_pam_perm_denied); - interop.allocator.free(lang_err_pam_session); - interop.allocator.free(lang_err_pam_sys); - interop.allocator.free(lang_err_pam_user_unknown); - interop.allocator.free(lang_err_path); - interop.allocator.free(lang_err_perm_dir); - interop.allocator.free(lang_err_perm_group); - interop.allocator.free(lang_err_perm_user); - interop.allocator.free(lang_err_pwnam); - interop.allocator.free(lang_err_user_gid); - interop.allocator.free(lang_err_user_init); - interop.allocator.free(lang_err_user_uid); - interop.allocator.free(lang_err_xsessions_dir); - interop.allocator.free(lang_err_xsessions_open); - interop.allocator.free(lang_f1); - interop.allocator.free(lang_f2); - interop.allocator.free(lang_login); - interop.allocator.free(lang_logout); - interop.allocator.free(lang_numlock); - interop.allocator.free(lang_password); - interop.allocator.free(lang_shell); - interop.allocator.free(lang_wayland); - interop.allocator.free(lang_xinitrc); - - main.allocator.free(lang_buffer); -} diff --git a/src/config/Config.zig b/src/config/Config.zig new file mode 100644 index 0000000..663e598 --- /dev/null +++ b/src/config/Config.zig @@ -0,0 +1,97 @@ +const build_options = @import("build_options"); +const enums = @import("../enums.zig"); + +const Animation = enums.Animation; +const Input = enums.Input; + +const Config = @This(); + +ly: struct { + animation: Animation, + asterisk: u8, + bg: u8, + bigclock: bool, + blank_box: bool, + blank_password: bool, + clock: []const u8, + console_dev: []const u8, + default_input: Input, + fg: u8, + hide_borders: bool, + hide_key_hints: bool, + input_len: u8, + lang: []const u8, + load: bool, + margin_box_h: u8, + margin_box_v: u8, + max_desktop_len: u8, + max_login_len: u8, + max_password_len: u8, + mcookie_cmd: []const u8, + min_refresh_delta: u16, + path: []const u8, + restart_cmd: []const u8, + restart_key: []const u8, + save: bool, + save_file: []const u8, + service_name: []const u8, + shutdown_cmd: []const u8, + shutdown_key: []const u8, + term_reset_cmd: []const u8, + term_restore_cursor_cmd: []const u8, + tty: u8, + wayland_cmd: []const u8, + wayland_specifier: bool, + waylandsessions: []const u8, + x_cmd: []const u8, + xinitrc: []const u8, + x_cmd_setup: []const u8, + xauth_cmd: []const u8, + xsessions: []const u8, +}, + +pub fn init() Config { + return .{ .ly = .{ + .animation = .none, + .asterisk = '*', + .bg = 0, + .bigclock = false, + .blank_box = true, + .blank_password = false, + .clock = "", + .console_dev = "/dev/console", + .default_input = .login, + .fg = 9, + .hide_borders = false, + .hide_key_hints = false, + .input_len = 34, + .lang = "en", + .load = true, + .margin_box_h = 2, + .margin_box_v = 1, + .max_desktop_len = 100, + .max_login_len = 255, + .max_password_len = 255, + .mcookie_cmd = "/usr/bin/mcookie", + .min_refresh_delta = 5, + .path = "/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin", + .restart_cmd = "/sbin/shutdown -r now", + .restart_key = "F2", + .save = true, + .save_file = "/etc/ly/save", + .service_name = "ly", + .shutdown_cmd = "/sbin/shutdown -a now", + .shutdown_key = "F1", + .term_reset_cmd = "/usr/bin/tput reset", + .term_restore_cursor_cmd = "/usr/bin/tput cnorm", + .tty = 2, + .wayland_cmd = build_options.data_directory ++ "/wsetup.sh", + .wayland_specifier = false, + .waylandsessions = "/usr/share/wayland-sessions", + .x_cmd = "/usr/bin/X", + .xinitrc = "~/.xinitrc", + .x_cmd_setup = build_options.data_directory ++ "/xsetup.sh", + .xauth_cmd = "/usr/bin/xauth", + .xsessions = "/usr/share/xsessions", + } }; +} diff --git a/src/config/ConfigReader.zig b/src/config/ConfigReader.zig new file mode 100644 index 0000000..d24d7b8 --- /dev/null +++ b/src/config/ConfigReader.zig @@ -0,0 +1,47 @@ +const std = @import("std"); +const ini = @import("ini"); +const Config = @import("Config.zig"); +const Lang = @import("Lang.zig"); + +const Allocator = std.mem.Allocator; + +pub const CONFIG_MAX_SIZE: u64 = 8 * 1024; + +const ConfigReader = @This(); + +allocator: Allocator, +config_allocated: bool = false, +lang_allocated: bool = false, +config: []u8 = undefined, +lang: []u8 = undefined, + +pub fn init(config_allocator: Allocator) ConfigReader { + return .{ + .allocator = config_allocator, + }; +} + +pub fn deinit(self: ConfigReader) void { + if (self.config_allocated) self.allocator.free(self.config); + if (self.lang_allocated) self.allocator.free(self.lang); +} + +pub fn readConfig(self: *ConfigReader, path: []const u8) !Config { + var file = std.fs.cwd().openFile(path, .{}) catch return Config.init(); + defer file.close(); + + self.config = try file.readToEndAlloc(self.allocator, CONFIG_MAX_SIZE); + self.config_allocated = true; + + return try ini.readToStruct(Config, self.config); +} + +pub fn readLang(self: *ConfigReader, path: []const u8) !Lang { + var file = std.fs.cwd().openFile(path, .{}) catch return Lang.init(); + defer file.close(); + + self.lang = try file.readToEndAlloc(self.allocator, CONFIG_MAX_SIZE); + self.lang_allocated = true; + + return try ini.readToStruct(Lang, self.lang); +} diff --git a/src/config/Lang.zig b/src/config/Lang.zig new file mode 100644 index 0000000..9c7ca6b --- /dev/null +++ b/src/config/Lang.zig @@ -0,0 +1,99 @@ +const Lang = @This(); + +ly: struct { + capslock: []const u8, + err_alloc: []const u8, + err_bounds: []const u8, + err_chdir: []const u8, + err_console_dev: []const u8, + err_dgn_oob: []const u8, + err_domain: []const u8, + err_hostname: []const u8, + err_mlock: []const u8, + err_null: []const u8, + err_pam: []const u8, + err_pam_abort: []const u8, + err_pam_acct_expired: []const u8, + err_pam_auth: []const u8, + err_pam_authinfo_unavail: []const u8, + err_pam_authok_reqd: []const u8, + err_pam_buf: []const u8, + err_pam_cred_err: []const u8, + err_pam_cred_expired: []const u8, + err_pam_cred_insufficient: []const u8, + err_pam_cred_unavail: []const u8, + err_pam_maxtries: []const u8, + err_pam_perm_denied: []const u8, + err_pam_session: []const u8, + err_pam_sys: []const u8, + err_pam_user_unknown: []const u8, + err_path: []const u8, + err_perm_dir: []const u8, + err_perm_group: []const u8, + err_perm_user: []const u8, + err_pwnam: []const u8, + err_user_gid: []const u8, + err_user_init: []const u8, + err_user_uid: []const u8, + err_xsessions_dir: []const u8, + err_xsessions_open: []const u8, + login: []const u8, + logout: []const u8, + numlock: []const u8, + password: []const u8, + restart: []const u8, + shell: []const u8, + shutdown: []const u8, + wayland: []const u8, + xinitrc: []const u8, +}, + +pub fn init() Lang { + return .{ .ly = .{ + .capslock = "capslock", + .err_alloc = "failed memory allocation", + .err_bounds = "out-of-bounds index", + .err_chdir = "failed to open home folder", + .err_console_dev = "failed to access console", + .err_dgn_oob = "log message", + .err_domain = "invalid domain", + .err_hostname = "failed to get hostname", + .err_mlock = "failed to lock password memory", + .err_null = "null pointer", + .err_pam = "pam transaction failed", + .err_pam_abort = "pam transaction aborted", + .err_pam_acct_expired = "account expired", + .err_pam_auth = "authentication error", + .err_pam_authinfo_unavail = "failed to get user info", + .err_pam_authok_reqd = "token expired", + .err_pam_buf = "memory buffer error", + .err_pam_cred_err = "failed to set credentials", + .err_pam_cred_expired = "credentials expired", + .err_pam_cred_insufficient = "insufficient credentials", + .err_pam_cred_unavail = "failed to get credentials", + .err_pam_maxtries = "reached maximum tries limit", + .err_pam_perm_denied = "permission denied", + .err_pam_session = "session error", + .err_pam_sys = "system error", + .err_pam_user_unknown = "unknown user", + .err_path = "failed to set path", + .err_perm_dir = "failed to change current directory", + .err_perm_group = "failed to downgrade group permissions", + .err_perm_user = "failed to downgrade user permissions", + .err_pwnam = "failed to get user info", + .err_user_gid = "failed to set user GID", + .err_user_init = "failed to initialize user", + .err_user_uid = "failed to set user UID", + .err_xsessions_dir = "failed to find sessions folder", + .err_xsessions_open = "failed to open sessions folder", + .login = "login:", + .logout = "logged out", + .numlock = "numlock", + .password = "password:", + .restart = "reboot", + .shell = "shell", + .shutdown = "shutdown", + .wayland = "wayland", + .xinitrc = "xinitrc", + } }; +} diff --git a/src/dragonfail_error.h b/src/dragonfail_error.h deleted file mode 100644 index f5ead2f..0000000 --- a/src/dragonfail_error.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef H_DRAGONFAIL_ERROR -#define H_DRAGONFAIL_ERROR - -enum dgn_error -{ - DGN_OK, // do not remove - - DGN_NULL, - DGN_ALLOC, - DGN_BOUNDS, - DGN_DOMAIN, - DGN_MLOCK, - DGN_XSESSIONS_DIR, - DGN_XSESSIONS_OPEN, - DGN_PATH, - DGN_CHDIR, - DGN_PWNAM, - DGN_USER_INIT, - DGN_USER_GID, - DGN_USER_UID, - DGN_PAM, - DGN_HOSTNAME, - - DGN_SIZE, // do not remove -}; - -#endif diff --git a/src/draw.c b/src/draw.c deleted file mode 100644 index 680cb50..0000000 --- a/src/draw.c +++ /dev/null @@ -1,997 +0,0 @@ -#include "dragonfail.h" -#include "termbox.h" - -#include "inputs.h" -#include "utils.h" -#include "config.h" -#include "draw.h" -#include "bigclock.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(__DragonFly__) || defined(__FreeBSD__) - #include -#else // linux - #include -#endif - -#define DOOM_STEPS 13 - -void draw_init(struct term_buf* buf) -{ - buf->width = tb_width(); - buf->height = tb_height(); - hostname(&buf->info_line); - - uint16_t len_login = strlen(lang.login); - uint16_t len_password = strlen(lang.password); - - if (len_login > len_password) - { - buf->labels_max_len = len_login; - } - else - { - buf->labels_max_len = len_password; - } - - buf->box_height = 7 + (2 * config.margin_box_v); - buf->box_width = - (2 * config.margin_box_h) - + (config.input_len + 1) - + buf->labels_max_len; - -#if defined(__linux__) || defined(__FreeBSD__) - buf->box_chars.left_up = 0x250c; - buf->box_chars.left_down = 0x2514; - buf->box_chars.right_up = 0x2510; - buf->box_chars.right_down = 0x2518; - buf->box_chars.top = 0x2500; - buf->box_chars.bot = 0x2500; - buf->box_chars.left = 0x2502; - buf->box_chars.right = 0x2502; -#else - buf->box_chars.left_up = '+'; - buf->box_chars.left_down = '+'; - buf->box_chars.right_up = '+'; - buf->box_chars.right_down= '+'; - buf->box_chars.top = '-'; - buf->box_chars.bot = '-'; - buf->box_chars.left = '|'; - buf->box_chars.right = '|'; -#endif -} - -static void doom_free(struct term_buf* buf); -static void matrix_free(struct term_buf* buf); - -void draw_free(struct term_buf* buf) -{ - if (config.animate) - { - switch (config.animation) - { - case 0: - doom_free(buf); - break; - case 1: - matrix_free(buf); - break; - } - } -} - -void draw_box(struct term_buf* buf) -{ - uint16_t box_x = (buf->width - buf->box_width) / 2; - uint16_t box_y = (buf->height - buf->box_height) / 2; - uint16_t box_x2 = (buf->width + buf->box_width) / 2; - uint16_t box_y2 = (buf->height + buf->box_height) / 2; - buf->box_x = box_x; - buf->box_y = box_y; - - if (!config.hide_borders) - { - // corners - tb_change_cell( - box_x - 1, - box_y - 1, - buf->box_chars.left_up, - config.fg, - config.bg); - tb_change_cell( - box_x2, - box_y - 1, - buf->box_chars.right_up, - config.fg, - config.bg); - tb_change_cell( - box_x - 1, - box_y2, - buf->box_chars.left_down, - config.fg, - config.bg); - tb_change_cell( - box_x2, - box_y2, - buf->box_chars.right_down, - config.fg, - config.bg); - - // top and bottom - struct tb_cell c1 = {buf->box_chars.top, config.fg, config.bg}; - struct tb_cell c2 = {buf->box_chars.bot, config.fg, config.bg}; - - for (uint16_t i = 0; i < buf->box_width; ++i) - { - tb_put_cell( - box_x + i, - box_y - 1, - &c1); - tb_put_cell( - box_x + i, - box_y2, - &c2); - } - - // left and right - c1.ch = buf->box_chars.left; - c2.ch = buf->box_chars.right; - - for (uint16_t i = 0; i < buf->box_height; ++i) - { - tb_put_cell( - box_x - 1, - box_y + i, - &c1); - - tb_put_cell( - box_x2, - box_y + i, - &c2); - } - } - - if (config.blank_box) - { - struct tb_cell blank = {' ', config.fg, config.bg}; - - for (uint16_t i = 0; i < buf->box_height; ++i) - { - for (uint16_t k = 0; k < buf->box_width; ++k) - { - tb_put_cell( - box_x + k, - box_y + i, - &blank); - } - } - } -} - -char* time_str(char* fmt, int maxlen) -{ - time_t timer; - char* buffer = malloc(maxlen); - struct tm* tm_info; - - timer = time(NULL); - tm_info = localtime(&timer); - - if (strftime(buffer, maxlen, fmt, tm_info) == 0) - { - buffer[0] = '\0'; - } - - return buffer; -} - -extern inline uint32_t* CLOCK_N(char c); - -struct tb_cell* clock_cell(char c) -{ - struct tb_cell* cells = malloc(sizeof(struct tb_cell) * CLOCK_W * CLOCK_H); - - struct timeval tv; - gettimeofday(&tv, NULL); - if (config.animate && c == ':' && tv.tv_usec / 500000) - { - c = ' '; - } - uint32_t* clockchars = CLOCK_N(c); - - for (int i = 0; i < CLOCK_W * CLOCK_H; i++) - { - cells[i].ch = clockchars[i]; - cells[i].fg = config.fg; - cells[i].bg = config.bg; - } - - return cells; -} - -void alpha_blit(struct tb_cell* buf, uint16_t x, uint16_t y, uint16_t w, uint16_t h, struct tb_cell* cells) -{ - if (x + w >= tb_width() || y + h >= tb_height()) - return; - - for (int i = 0; i < h; i++) - { - for (int j = 0; j < w; j++) - { - struct tb_cell cell = cells[i * w + j]; - if (cell.ch) - { - buf[(y + i) * tb_width() + (x + j)] = cell; - } - } - } -} - -void draw_bigclock(struct term_buf* buf) -{ - if (!config.bigclock) - { - return; - } - - int xo = buf->width / 2 - (5 * (CLOCK_W + 1)) / 2; - int yo = (buf->height - buf->box_height) / 2 - CLOCK_H - 2; - - char* clockstr = time_str("%H:%M", 6); - struct tb_cell* clockcell; - - for (int i = 0; i < 5; i++) - { - clockcell = clock_cell(clockstr[i]); - alpha_blit(tb_cell_buffer(), xo + i * (CLOCK_W + 1), yo, CLOCK_W, CLOCK_H, clockcell); - free(clockcell); - } - - free(clockstr); -} - -void draw_clock(struct term_buf* buf) -{ - if (config.clock == NULL || strlen(config.clock) == 0) - { - return; - } - - char* clockstr = time_str(config.clock, 32); - int clockstrlen = strlen(clockstr); - - struct tb_cell* cells = strn_cell(clockstr, clockstrlen); - tb_blit(buf->width - clockstrlen, 0, clockstrlen, 1, cells); - - free(clockstr); - free(cells); -} - -struct tb_cell* strn_cell(char* s, uint16_t len) // throws -{ - struct tb_cell* cells = malloc((sizeof (struct tb_cell)) * len); - char* s2 = s; - uint32_t c; - - if (cells != NULL) - { - for (uint16_t i = 0; i < len; ++i) - { - if ((s2 - s) >= len) - { - break; - } - - s2 += utf8_char_to_unicode(&c, s2); - - cells[i].ch = c; - cells[i].bg = config.bg; - cells[i].fg = config.fg; - } - } - else - { - dgn_throw(DGN_ALLOC); - } - - return cells; -} - -struct tb_cell* str_cell(char* s) // throws -{ - return strn_cell(s, strlen(s)); -} - -void draw_labels(struct term_buf* buf) // throws -{ - // login text - struct tb_cell* login = str_cell(lang.login); - - if (dgn_catch()) - { - dgn_reset(); - } - else - { - tb_blit( - buf->box_x + config.margin_box_h, - buf->box_y + config.margin_box_v + 4, - strlen(lang.login), - 1, - login); - free(login); - } - - // password text - struct tb_cell* password = str_cell(lang.password); - - if (dgn_catch()) - { - dgn_reset(); - } - else - { - tb_blit( - buf->box_x + config.margin_box_h, - buf->box_y + config.margin_box_v + 6, - strlen(lang.password), - 1, - password); - free(password); - } - - if (buf->info_line != NULL) - { - uint16_t len = strlen(buf->info_line); - struct tb_cell* info_cell = str_cell(buf->info_line); - - if (dgn_catch()) - { - dgn_reset(); - } - else - { - tb_blit( - buf->box_x + ((buf->box_width - len) / 2), - buf->box_y + config.margin_box_v, - len, - 1, - info_cell); - free(info_cell); - } - } -} - -void draw_f_commands() -{ - struct tb_cell* f1 = str_cell(lang.f1); - - if (dgn_catch()) - { - dgn_reset(); - } - else - { - tb_blit(0, 0, strlen(lang.f1), 1, f1); - free(f1); - } - - struct tb_cell* f2 = str_cell(lang.f2); - - if (dgn_catch()) - { - dgn_reset(); - } - else - { - tb_blit(strlen(lang.f1) + 1, 0, strlen(lang.f2), 1, f2); - free(f2); - } -} - -void draw_lock_state(struct term_buf* buf) -{ - // get values - int fd = open(config.console_dev, O_RDONLY); - - if (fd < 0) - { - buf->info_line = lang.err_console_dev; - return; - } - - bool numlock_on; - bool capslock_on; - -#if defined(__DragonFly__) || defined(__FreeBSD__) - int led; - ioctl(fd, KDGETLED, &led); - numlock_on = led & LED_NUM; - capslock_on = led & LED_CAP; -#else // linux - char led; - ioctl(fd, KDGKBLED, &led); - numlock_on = led & K_NUMLOCK; - capslock_on = led & K_CAPSLOCK; -#endif - - close(fd); - - // print text - uint16_t pos_x = buf->width - strlen(lang.numlock); - - if (numlock_on) - { - struct tb_cell* numlock = str_cell(lang.numlock); - - if (dgn_catch()) - { - dgn_reset(); - } - else - { - tb_blit(pos_x, 0, strlen(lang.numlock), 1, numlock); - free(numlock); - } - } - - pos_x -= strlen(lang.capslock) + 1; - - if (capslock_on) - { - struct tb_cell* capslock = str_cell(lang.capslock); - - if (dgn_catch()) - { - dgn_reset(); - } - else - { - tb_blit(pos_x, 0, strlen(lang.capslock), 1, capslock); - free(capslock); - } - } -} - -void draw_desktop(struct desktop* target) -{ - uint16_t len = strlen(target->list[target->cur]); - - if (len > (target->visible_len - 3)) - { - len = target->visible_len - 3; - } - - tb_change_cell( - target->x, - target->y, - '<', - config.fg, - config.bg); - - tb_change_cell( - target->x + target->visible_len - 1, - target->y, - '>', - config.fg, - config.bg); - - for (uint16_t i = 0; i < len; ++ i) - { - tb_change_cell( - target->x + i + 2, - target->y, - target->list[target->cur][i], - config.fg, - config.bg); - } -} - -void draw_input(struct text* input) -{ - uint16_t len = strlen(input->text); - uint16_t visible_len = input->visible_len; - - if (len > visible_len) - { - len = visible_len; - } - - struct tb_cell* cells = strn_cell(input->visible_start, len); - - if (dgn_catch()) - { - dgn_reset(); - } - else - { - tb_blit(input->x, input->y, len, 1, cells); - free(cells); - - struct tb_cell c1 = {' ', config.fg, config.bg}; - - for (uint16_t i = input->end - input->visible_start; i < visible_len; ++i) - { - tb_put_cell( - input->x + i, - input->y, - &c1); - } - } -} - -void draw_input_mask(struct text* input) -{ - uint16_t len = strlen(input->text); - uint16_t visible_len = input->visible_len; - - if (len > visible_len) - { - len = visible_len; - } - - struct tb_cell c1 = {config.asterisk, config.fg, config.bg}; - struct tb_cell c2 = {' ', config.fg, config.bg}; - - for (uint16_t i = 0; i < visible_len; ++i) - { - if (input->visible_start + i < input->end) - { - tb_put_cell( - input->x + i, - input->y, - &c1); - } - else - { - tb_put_cell( - input->x + i, - input->y, - &c2); - } - } -} - -void position_input( - struct term_buf* buf, - struct desktop* desktop, - struct text* login, - struct text* password) -{ - uint16_t x = buf->box_x + config.margin_box_h + buf->labels_max_len + 1; - int32_t len = buf->box_x + buf->box_width - config.margin_box_h - x; - - if (len < 0) - { - return; - } - - desktop->x = x; - desktop->y = buf->box_y + config.margin_box_v + 2; - desktop->visible_len = len; - - login->x = x; - login->y = buf->box_y + config.margin_box_v + 4; - login->visible_len = len; - - password->x = x; - password->y = buf->box_y + config.margin_box_v + 6; - password->visible_len = len; -} - -static void doom_init(struct term_buf* buf) -{ - buf->init_width = buf->width; - buf->init_height = buf->height; - buf->astate.doom = malloc(sizeof(struct doom_state)); - - if (buf->astate.doom == NULL) - { - dgn_throw(DGN_ALLOC); - } - - uint16_t tmp_len = buf->width * buf->height; - buf->astate.doom->buf = malloc(tmp_len); - tmp_len -= buf->width; - - if (buf->astate.doom->buf == NULL) - { - dgn_throw(DGN_ALLOC); - } - - memset(buf->astate.doom->buf, 0, tmp_len); - memset(buf->astate.doom->buf + tmp_len, DOOM_STEPS - 1, buf->width); -} - -static void doom_free(struct term_buf* buf) -{ - free(buf->astate.doom->buf); - free(buf->astate.doom); -} - -// Adapted from cmatrix -static void matrix_init(struct term_buf* buf) -{ - buf->init_width = buf->width; - buf->init_height = buf->height; - buf->astate.matrix = malloc(sizeof(struct matrix_state)); - struct matrix_state* s = buf->astate.matrix; - - if (s == NULL) - { - dgn_throw(DGN_ALLOC); - } - - uint16_t len = buf->height + 1; - s->grid = malloc(sizeof(struct matrix_dot*) * len); - - if (s->grid == NULL) - { - dgn_throw(DGN_ALLOC); - } - - len = (buf->height + 1) * buf->width; - (s->grid)[0] = malloc(sizeof(struct matrix_dot) * len); - - if ((s->grid)[0] == NULL) - { - dgn_throw(DGN_ALLOC); - } - - for (int i = 1; i <= buf->height; ++i) - { - s->grid[i] = s->grid[i - 1] + buf->width; - - if (s->grid[i] == NULL) - { - dgn_throw(DGN_ALLOC); - } - } - - s->length = malloc(buf->width * sizeof(int)); - - if (s->length == NULL) - { - dgn_throw(DGN_ALLOC); - } - - s->spaces = malloc(buf->width * sizeof(int)); - - if (s->spaces == NULL) - { - dgn_throw(DGN_ALLOC); - } - - s->updates = malloc(buf->width * sizeof(int)); - - if (s->updates == NULL) - { - dgn_throw(DGN_ALLOC); - } - - // Initialize grid - for (int i = 0; i <= buf->height; ++i) - { - for (int j = 0; j <= buf->width - 1; j += 2) - { - s->grid[i][j].val = -1; - } - } - - for (int j = 0; j < buf->width; j += 2) - { - s->spaces[j] = (int) rand() % buf->height + 1; - s->length[j] = (int) rand() % (buf->height - 3) + 3; - s->grid[1][j].val = ' '; - s->updates[j] = (int) rand() % 3 + 1; - } -} - -static void matrix_free(struct term_buf* buf) -{ - free(buf->astate.matrix->grid[0]); - free(buf->astate.matrix->grid); - free(buf->astate.matrix->length); - free(buf->astate.matrix->spaces); - free(buf->astate.matrix->updates); - free(buf->astate.matrix); -} - -void animate_init(struct term_buf* buf) -{ - if (config.animate) - { - switch(config.animation) - { - case 0: - { - doom_init(buf); - break; - } - case 1: - { - matrix_init(buf); - break; - } - } - } -} - -static void doom(struct term_buf* term_buf) -{ - static struct tb_cell fire[DOOM_STEPS] = - { - {' ', 9, 0}, // default - {0x2591, 2, 0}, // red - {0x2592, 2, 0}, // red - {0x2593, 2, 0}, // red - {0x2588, 2, 0}, // red - {0x2591, 4, 2}, // yellow - {0x2592, 4, 2}, // yellow - {0x2593, 4, 2}, // yellow - {0x2588, 4, 2}, // yellow - {0x2591, 8, 4}, // white - {0x2592, 8, 4}, // white - {0x2593, 8, 4}, // white - {0x2588, 8, 4}, // white - }; - - uint16_t src; - uint16_t random; - uint16_t dst; - - uint16_t w = term_buf->init_width; - uint8_t* tmp = term_buf->astate.doom->buf; - - if ((term_buf->width != term_buf->init_width) || (term_buf->height != term_buf->init_height)) - { - return; - } - - struct tb_cell* buf = tb_cell_buffer(); - - for (uint16_t x = 0; x < w; ++x) - { - for (uint16_t y = 1; y < term_buf->init_height; ++y) - { - src = y * w + x; - random = ((rand() % 7) & 3); - dst = src - random + 1; - - if (w > dst) - { - dst = 0; - } - else - { - dst -= w; - } - - tmp[dst] = tmp[src] - (random & 1); - - if (tmp[dst] > 12) - { - tmp[dst] = 0; - } - - buf[dst] = fire[tmp[dst]]; - buf[src] = fire[tmp[src]]; - } - } -} - -// Adapted from cmatrix -static void matrix(struct term_buf* buf) -{ - static int frame = 3; - const int frame_delay = 8; - static int count = 0; - bool first_col; - struct matrix_state* s = buf->astate.matrix; - - // Allowed codepoints - const int randmin = 33; - const int randnum = 123 - randmin; - // Chars change mid-scroll - const bool changes = true; - - if ((buf->width != buf->init_width) || (buf->height != buf->init_height)) - { - return; - } - - count += 1; - if (count > frame_delay) - { - frame += 1; - if (frame > 4) frame = 1; - count = 0; - - for (int j = 0; j < buf->width; j += 2) - { - int tail; - if (frame > s->updates[j]) - { - if (s->grid[0][j].val == -1 && s->grid[1][j].val == ' ') - { - if (s->spaces[j] > 0) - { - s->spaces[j]--; - } else { - s->length[j] = (int) rand() % (buf->height - 3) + 3; - s->grid[0][j].val = (int) rand() % randnum + randmin; - s->spaces[j] = (int) rand() % buf->height + 1; - } - } - - int i = 0, seg_len = 0; - first_col = 1; - while (i <= buf->height) - { - // Skip over spaces - while (i <= buf->height - && (s->grid[i][j].val == ' ' || s->grid[i][j].val == -1)) - { - i++; - } - - if (i > buf->height) break; - - // Find the head of this col - tail = i; - seg_len = 0; - while (i <= buf->height - && (s->grid[i][j].val != ' ' && s->grid[i][j].val != -1)) - { - s->grid[i][j].is_head = false; - if (changes) - { - if (rand() % 8 == 0) - s->grid[i][j].val = (int) rand() % randnum + randmin; - } - i++; - seg_len++; - } - - // Head's down offscreen - if (i > buf->height) - { - s->grid[tail][j].val = ' '; - continue; - } - - s->grid[i][j].val = (int) rand() % randnum + randmin; - s->grid[i][j].is_head = true; - - if (seg_len > s->length[j] || !first_col) { - s->grid[tail][j].val = ' '; - s->grid[0][j].val = -1; - } - first_col = 0; - i++; - } - } - } - } - - uint32_t blank; - utf8_char_to_unicode(&blank, " "); - - for (int j = 0; j < buf->width; j += 2) - { - for (int i = 1; i <= buf->height; ++i) - { - uint32_t c; - int fg = TB_GREEN; - int bg = TB_DEFAULT; - - if (s->grid[i][j].val == -1 || s->grid[i][j].val == ' ') - { - tb_change_cell(j, i - 1, blank, fg, bg); - continue; - } - - char tmp[2]; - tmp[0] = s->grid[i][j].val; - tmp[1] = '\0'; - if(utf8_char_to_unicode(&c, tmp)) - { - if (s->grid[i][j].is_head) - { - fg = TB_WHITE | TB_BOLD; - } - tb_change_cell(j, i - 1, c, fg, bg); - } - } - } -} - -void animate(struct term_buf* buf) -{ - buf->width = tb_width(); - buf->height = tb_height(); - - if (config.animate) - { - switch(config.animation) - { - case 0: - { - doom(buf); - break; - } - case 1: - { - matrix(buf); - break; - } - } - } -} - -bool cascade(struct term_buf* term_buf, uint8_t* fails) -{ - uint16_t width = term_buf->width; - uint16_t height = term_buf->height; - - struct tb_cell* buf = tb_cell_buffer(); - bool changes = false; - char c_under; - char c; - - for (int i = height - 2; i >= 0; --i) - { - for (int k = 0; k < width; ++k) - { - c = buf[i * width + k].ch; - - if (isspace(c)) - { - continue; - } - - c_under = buf[(i + 1) * width + k].ch; - - if (!isspace(c_under)) - { - continue; - } - - if (!changes) - { - changes = true; - } - - if ((rand() % 10) > 7) - { - continue; - } - - buf[(i + 1) * width + k] = buf[i * width + k]; - buf[i * width + k].ch = ' '; - } - } - - // stop force-updating - if (!changes) - { - sleep(7); - *fails = 0; - - return false; - } - - // force-update - return true; -} diff --git a/src/draw.h b/src/draw.h deleted file mode 100644 index 0e6d164..0000000 --- a/src/draw.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef H_LY_DRAW -#define H_LY_DRAW - -#include "termbox.h" -#include "inputs.h" - -#include -#include - -struct box -{ - uint32_t left_up; - uint32_t left_down; - uint32_t right_up; - uint32_t right_down; - uint32_t top; - uint32_t bot; - uint32_t left; - uint32_t right; -}; - -struct matrix_dot -{ - int val; - bool is_head; -}; - -struct matrix_state -{ - struct matrix_dot** grid; - int* length; - int* spaces; - int* updates; -}; - -struct doom_state -{ - uint8_t* buf; -}; - -union anim_state -{ - struct doom_state* doom; - struct matrix_state* matrix; -}; - -struct term_buf -{ - uint16_t width; - uint16_t height; - uint16_t init_width; - uint16_t init_height; - - struct box box_chars; - char* info_line; - uint16_t labels_max_len; - uint16_t box_x; - uint16_t box_y; - uint16_t box_width; - uint16_t box_height; - - union anim_state astate; -}; - -void draw_init(struct term_buf* buf); -void draw_free(struct term_buf* buf); -void draw_box(struct term_buf* buf); - -struct tb_cell* strn_cell(char* s, uint16_t len); -struct tb_cell* str_cell(char* s); - -void draw_labels(struct term_buf* buf); -void draw_f_commands(); -void draw_lock_state(struct term_buf* buf); -void draw_desktop(struct desktop* target); -void draw_input(struct text* input); -void draw_input_mask(struct text* input); - -void position_input( - struct term_buf* buf, - struct desktop* desktop, - struct text* login, - struct text* password); - -void animate_init(struct term_buf* buf); -void animate(struct term_buf* buf); -bool cascade(struct term_buf* buf, uint8_t* fails); - -void draw_bigclock(struct term_buf *buf); -void draw_clock(struct term_buf *buf); - -#endif diff --git a/src/enums.zig b/src/enums.zig new file mode 100644 index 0000000..4cfa564 --- /dev/null +++ b/src/enums.zig @@ -0,0 +1,18 @@ +pub const Animation = enum { + none, + doom, + matrix, +}; + +pub const DisplayServer = enum { + wayland, + shell, + xinitrc, + x11, +}; + +pub const Input = enum { + session, + login, + password, +}; diff --git a/src/inputs.c b/src/inputs.c deleted file mode 100644 index 1753677..0000000 --- a/src/inputs.c +++ /dev/null @@ -1,285 +0,0 @@ -#include "dragonfail.h" -#include "termbox.h" -#include "inputs.h" -#include "config.h" - -#include -#include -#include -#include -#include - -void handle_desktop(void* input_struct, struct tb_event* event) -{ - struct desktop* target = (struct desktop*) input_struct; - - if ((event != NULL) && (event->type == TB_EVENT_KEY)) - { - if (event->key == TB_KEY_ARROW_LEFT || (event->key == TB_KEY_CTRL_H)) - { - input_desktop_right(target); - } - else if (event->key == TB_KEY_ARROW_RIGHT || (event->key == TB_KEY_CTRL_L)) - { - input_desktop_left(target); - } - } - - tb_set_cursor(target->x + 2, target->y); -} - -void handle_text(void* input_struct, struct tb_event* event) -{ - struct text* target = (struct text*) input_struct; - - if ((event != NULL) && (event->type == TB_EVENT_KEY)) - { - if (event->key == TB_KEY_ARROW_LEFT) - { - input_text_left(target); - } - else if (event->key == TB_KEY_ARROW_RIGHT) - { - input_text_right(target); - } - else if (event->key == TB_KEY_DELETE) - { - input_text_delete(target); - } - else if ((event->key == TB_KEY_BACKSPACE) - || (event->key == TB_KEY_BACKSPACE2)) - { - input_text_backspace(target); - } - else if (((event->ch > 31) && (event->ch < 127)) - || (event->key == TB_KEY_SPACE)) - { - char buf[7] = {0}; - - if (event->key == TB_KEY_SPACE) - { - buf[0] = ' '; - } - else - { - utf8_unicode_to_char(buf, event->ch); - } - - input_text_write(target, buf[0]); - } - } - - tb_set_cursor( - target->x + (target->cur - target->visible_start), - target->y); -} - -void input_desktop(struct desktop* target) -{ - target->list = NULL; - target->list_simple = NULL; - target->cmd = NULL; - target->display_server = NULL; - target->cur = 0; - target->len = 0; - - input_desktop_add(target, strdup(lang.shell), strdup(""), DS_SHELL); - input_desktop_add(target, strdup(lang.xinitrc), strdup(config.xinitrc), DS_XINITRC); -#if 0 - input_desktop_add(target, strdup(lang.wayland), strdup(""), DS_WAYLAND); -#endif -} - -void input_text(struct text* target, uint64_t len) -{ - target->text = malloc(len + 1); - - if (target->text == NULL) - { - dgn_throw(DGN_ALLOC); - return; - } - else - { - int ok = mlock(target->text, len + 1); - - if (ok < 0) - { - dgn_throw(DGN_MLOCK); - return; - } - - memset(target->text, 0, len + 1); - } - - target->cur = target->text; - target->end = target->text; - target->visible_start = target->text; - target->len = len; - target->x = 0; - target->y = 0; -} - -void input_desktop_free(struct desktop* target) -{ - if (target != NULL) - { - for (uint16_t i = 0; i < target->len; ++i) - { - if (target->list[i] != NULL) - { - free(target->list[i]); - } - - if (target->cmd[i] != NULL) - { - free(target->cmd[i]); - } - } - - free(target->list); - free(target->cmd); - free(target->display_server); - } -} - -void input_text_free(struct text* target) -{ - memset(target->text, 0, target->len); - munlock(target->text, target->len + 1); - free(target->text); -} - -void input_desktop_right(struct desktop* target) -{ - ++(target->cur); - - if (target->cur >= target->len) - { - target->cur = 0; - } -} - -void input_desktop_left(struct desktop* target) -{ - --(target->cur); - - if (target->cur >= target->len) - { - target->cur = target->len - 1; - } -} - -void input_desktop_add( - struct desktop* target, - char* name, - char* cmd, - enum display_server display_server) -{ - ++(target->len); - target->list = realloc(target->list, target->len * (sizeof (char*))); - target->list_simple = realloc(target->list_simple, target->len * (sizeof (char*))); - target->cmd = realloc(target->cmd, target->len * (sizeof (char*))); - target->display_server = realloc( - target->display_server, - target->len * (sizeof (enum display_server))); - target->cur = target->len - 1; - - if ((target->list == NULL) - || (target->cmd == NULL) - || (target->display_server == NULL)) - { - dgn_throw(DGN_ALLOC); - return; - } - - target->list[target->cur] = name; - - int name_len = strlen(name); - char* name_simple = strdup(name); - - if (strstr(name_simple, " ") != NULL) - { - name_simple = strtok(name_simple, " "); - } - - for (int i = 0; i < name_len; i++) - { - name_simple[i] = tolower(name_simple[i]); - } - - target->list_simple[target->cur] = name_simple; - target->cmd[target->cur] = cmd; - target->display_server[target->cur] = display_server; -} - -void input_text_right(struct text* target) -{ - if (target->cur < target->end) - { - ++(target->cur); - - if ((target->cur - target->visible_start) > target->visible_len) - { - ++(target->visible_start); - } - } -} - -void input_text_left(struct text* target) -{ - if (target->cur > target->text) - { - --(target->cur); - - if ((target->cur - target->visible_start) < 0) - { - --(target->visible_start); - } - } -} - -void input_text_write(struct text* target, char ascii) -{ - if (ascii <= 0) - { - return; // unices do not support usernames and passwords other than ascii - } - - if ((target->end - target->text + 1) < target->len) - { - // moves the text to the right to add space for the new ascii char - memcpy(target->cur + 1, target->cur, target->end - target->cur); - ++(target->end); - // adds the new char and moves the cursor to the right - *(target->cur) = ascii; - input_text_right(target); - } -} - -void input_text_delete(struct text* target) -{ - if (target->cur < target->end) - { - // moves the text on the right to overwrite the currently pointed char - memcpy(target->cur, target->cur + 1, target->end - target->cur + 1); - --(target->end); - } -} - -void input_text_backspace(struct text* target) -{ - if (target->cur > target->text) - { - input_text_left(target); - input_text_delete(target); - } -} - -void input_text_clear(struct text* target) -{ - memset(target->text, 0, target->len + 1); - target->cur = target->text; - target->end = target->text; - target->visible_start = target->text; -} diff --git a/src/inputs.h b/src/inputs.h deleted file mode 100644 index d0f0f61..0000000 --- a/src/inputs.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef H_LY_INPUTS -#define H_LY_INPUTS - -#include "termbox.h" - -#include - -enum display_server {DS_WAYLAND, DS_SHELL, DS_XINITRC, DS_XORG}; - -struct text -{ - char* text; - char* end; - int64_t len; - char* cur; - char* visible_start; - uint16_t visible_len; - - uint16_t x; - uint16_t y; -}; - -struct desktop -{ - char** list; - char** list_simple; - char** cmd; - enum display_server* display_server; - - uint16_t cur; - uint16_t len; - uint16_t visible_len; - uint16_t x; - uint16_t y; -}; - -void handle_desktop(void* input_struct, struct tb_event* event); -void handle_text(void* input_struct, struct tb_event* event); -void input_desktop(struct desktop* target); -void input_text(struct text* target, uint64_t len); -void input_desktop_free(struct desktop* target); -void input_text_free(struct text* target); -void input_desktop_right(struct desktop* target); -void input_desktop_left(struct desktop* target); -void input_desktop_add( - struct desktop* target, - char* name, - char* cmd, - enum display_server display_server); -void input_text_right(struct text* target); -void input_text_left(struct text* target); -void input_text_write(struct text* target, char ascii); -void input_text_delete(struct text* target); -void input_text_backspace(struct text* target); -void input_text_clear(struct text* target); - -#endif diff --git a/src/interop.zig b/src/interop.zig index 3cbe2fa..3156794 100644 --- a/src/interop.zig +++ b/src/interop.zig @@ -1,13 +1,125 @@ const std = @import("std"); +const builtin = @import("builtin"); +const Allocator = std.mem.Allocator; -pub const allocator = std.heap.raw_c_allocator; +pub const termbox = @cImport({ + @cInclude("termbox.h"); +}); -pub fn c_str(str: []const u8) ![:0]u8 { - const new_str = try allocator.allocSentinel(u8, str.len, 0); +pub const pam = @cImport({ + @cInclude("security/pam_appl.h"); +}); - for (str, 0..) |c, i| { - new_str[i] = c; +pub const c_size = u64; +pub const c_uid = u32; +pub const c_time = c_long; +pub const tm = extern struct { + tm_sec: c_int, + tm_min: c_int, + tm_hour: c_int, + tm_mday: c_int, + tm_mon: c_int, + tm_year: c_int, + tm_wday: c_int, + tm_yday: c_int, + tm_isdst: c_int, +}; + +pub const _POSIX_HOST_NAME_MAX: c_int = 0xFF; +pub const _SC_HOST_NAME_MAX: c_int = 0xB4; + +pub const VT_ACTIVATE: c_int = 0x5606; +pub const VT_WAITACTIVE: c_int = 0x5607; + +pub const KDGETLED: c_int = 0x4B31; +pub const KDGKBLED: c_int = 0x4B64; + +pub const LED_NUM: c_int = 0x02; +pub const LED_CAP: c_int = 0x04; + +pub const K_NUMLOCK: c_int = 0x02; +pub const K_CAPSLOCK: c_int = 0x04; + +pub const O_RDONLY: c_uint = 0x00; +pub const O_WRONLY: c_uint = 0x01; +pub const O_RDWR: c_uint = 0x02; + +pub extern "c" fn fileno(stream: *std.c.FILE) c_int; +pub extern "c" fn sysconf(name: c_int) c_long; +pub extern "c" fn time(second: ?*c_time) c_time; +pub extern "c" fn localtime(timer: *const c_time) *tm; +pub extern "c" fn strftime(str: [*:0]u8, maxsize: c_size, format: [*:0]const u8, timeptr: *const tm) c_size; +pub extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int; +pub extern "c" fn getuid() c_uid; + +pub fn getHostName(allocator: Allocator) !struct { + buffer: []u8, + slice: []const u8, +} { + const hostname_sysconf = sysconf(_SC_HOST_NAME_MAX); + const hostname_max_length: u64 = if (hostname_sysconf < 0) @intCast(_POSIX_HOST_NAME_MAX) else @intCast(hostname_sysconf); + + const buffer = try allocator.alloc(u8, hostname_max_length); + + const error_code = std.c.gethostname(buffer.ptr, hostname_max_length); + if (error_code < 0) return error.CannotGetHostName; + + var hostname_length: u64 = 0; + for (buffer, 0..) |char, i| { + if (char == 0) { + hostname_length = i + 1; + break; + } + } + + return .{ + .buffer = buffer, + .slice = buffer[0..hostname_length], + }; +} + +pub fn timeAsString(allocator: Allocator, format: []const u8, max_length: u64) ![:0]u8 { + const timer = time(null); + const tm_info = localtime(&timer); + const buffer = try allocator.allocSentinel(u8, max_length, 0); + + const format_z = try allocator.dupeZ(u8, format); + defer allocator.free(format_z); + + if (strftime(buffer, max_length, format_z, tm_info) < 0) return error.CannotGetFormattedTime; + + return buffer; +} + +pub fn getLockState(allocator: Allocator, console_dev: []const u8) !struct { + numlock: bool, + capslock: bool, +} { + const console_dev_z = try allocator.dupeZ(u8, console_dev); + defer allocator.free(console_dev_z); + + const fd = std.c.open(console_dev_z, O_RDONLY); + if (fd < 0) return error.CannotOpenConsoleDev; + + var numlock = false; + var capslock = false; + + if (builtin.os.tag.isBSD()) { + var led: c_int = undefined; + _ = std.c.ioctl(fd, KDGETLED, &led); + numlock = (led & LED_NUM) != 0; + capslock = (led & LED_CAP) != 0; + } else { + var led: c_char = undefined; + _ = std.c.ioctl(fd, KDGKBLED, &led); + numlock = (led & K_NUMLOCK) != 0; + capslock = (led & K_CAPSLOCK) != 0; } - return new_str; + _ = std.c.close(fd); + + return .{ + .numlock = numlock, + .capslock = capslock, + }; } diff --git a/src/login.c b/src/login.c deleted file mode 100644 index ece1ee6..0000000 --- a/src/login.c +++ /dev/null @@ -1,715 +0,0 @@ -#include "dragonfail.h" -#include "termbox.h" - -#include "inputs.h" -#include "draw.h" -#include "utils.h" -#include "config.h" -#include "login.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int get_free_display() -{ - char xlock[1024]; - uint8_t i; - - for (i = 0; i < 200; ++i) - { - snprintf(xlock, 1024, "/tmp/.X%d-lock", i); - - if (access(xlock, F_OK) == -1) - { - break; - } - } - - return i; -} - -void reset_terminal(struct passwd* pwd) -{ - pid_t pid = fork(); - - if (pid == 0) - { - execl(pwd->pw_shell, pwd->pw_shell, "-c", config.term_reset_cmd, NULL); - exit(EXIT_SUCCESS); - } - - int status; - waitpid(pid, &status, 0); -} - -int login_conv( - int num_msg, - const struct pam_message** msg, - struct pam_response** resp, - void* appdata_ptr) -{ - *resp = calloc(num_msg, sizeof (struct pam_response)); - - if (*resp == NULL) - { - return PAM_BUF_ERR; - } - - char* username; - char* password; - int ok = PAM_SUCCESS; - int i; - - for (i = 0; i < num_msg; ++i) - { - switch (msg[i]->msg_style) - { - case PAM_PROMPT_ECHO_ON: - { - username = ((char**) appdata_ptr)[0]; - (*resp)[i].resp = strdup(username); - break; - } - case PAM_PROMPT_ECHO_OFF: - { - password = ((char**) appdata_ptr)[1]; - (*resp)[i].resp = strdup(password); - break; - } - case PAM_ERROR_MSG: - { - ok = PAM_CONV_ERR; - break; - } - } - - if (ok != PAM_SUCCESS) - { - break; - } - } - - if (ok != PAM_SUCCESS) - { - for (i = 0; i < num_msg; ++i) - { - if ((*resp)[i].resp == NULL) - { - continue; - } - - free((*resp)[i].resp); - (*resp)[i].resp = NULL; - } - - free(*resp); - *resp = NULL; - } - - return ok; -} - -void pam_diagnose(int error, struct term_buf* buf) -{ - switch (error) - { - case PAM_ACCT_EXPIRED: - { - buf->info_line = lang.err_pam_acct_expired; - break; - } - case PAM_AUTH_ERR: - { - buf->info_line = lang.err_pam_auth; - break; - } - case PAM_AUTHINFO_UNAVAIL: - { - buf->info_line = lang.err_pam_authinfo_unavail; - break; - } - case PAM_BUF_ERR: - { - buf->info_line = lang.err_pam_buf; - break; - } - case PAM_CRED_ERR: - { - buf->info_line = lang.err_pam_cred_err; - break; - } - case PAM_CRED_EXPIRED: - { - buf->info_line = lang.err_pam_cred_expired; - break; - } - case PAM_CRED_INSUFFICIENT: - { - buf->info_line = lang.err_pam_cred_insufficient; - break; - } - case PAM_CRED_UNAVAIL: - { - buf->info_line = lang.err_pam_cred_unavail; - break; - } - case PAM_MAXTRIES: - { - buf->info_line = lang.err_pam_maxtries; - break; - } - case PAM_NEW_AUTHTOK_REQD: - { - buf->info_line = lang.err_pam_authok_reqd; - break; - } - case PAM_PERM_DENIED: - { - buf->info_line = lang.err_pam_perm_denied; - break; - } - case PAM_SESSION_ERR: - { - buf->info_line = lang.err_pam_session; - break; - } - case PAM_SYSTEM_ERR: - { - buf->info_line = lang.err_pam_sys; - break; - } - case PAM_USER_UNKNOWN: - { - buf->info_line = lang.err_pam_user_unknown; - break; - } - case PAM_ABORT: - default: - { - buf->info_line = lang.err_pam_abort; - break; - } - } - - dgn_throw(DGN_PAM); -} - -void env_init(struct passwd* pwd) -{ - extern char** environ; - char* term = getenv("TERM"); - char* lang = getenv("LANG"); - // clean env - environ[0] = NULL; - - setenv("TERM", term ? term : "linux", 1); - setenv("HOME", pwd->pw_dir, 1); - setenv("PWD", pwd->pw_dir, 1); - setenv("SHELL", pwd->pw_shell, 1); - setenv("USER", pwd->pw_name, 1); - setenv("LOGNAME", pwd->pw_name, 1); - setenv("LANG", lang ? lang : "C", 1); - - // Set PATH if specified in the configuration - if (strlen(config.path)) - { - int ok = setenv("PATH", config.path, 1); - - if (ok != 0) - { - dgn_throw(DGN_PATH); - } - } -} - -void env_xdg_session(const enum display_server display_server) -{ - switch (display_server) - { - case DS_WAYLAND: - { - setenv("XDG_SESSION_TYPE", "wayland", 1); - break; - } - case DS_SHELL: - { - setenv("XDG_SESSION_TYPE", "tty", 0); - break; - } - case DS_XINITRC: - case DS_XORG: - { - setenv("XDG_SESSION_TYPE", "x11", 0); - break; - } - } -} - -void env_xdg(const char* tty_id, const char* desktop_name) -{ - char user[20]; - snprintf(user, 20, "/run/user/%d", getuid()); - setenv("XDG_RUNTIME_DIR", user, 0); - setenv("XDG_SESSION_CLASS", "user", 0); - setenv("XDG_SESSION_ID", "1", 0); - setenv("XDG_SESSION_DESKTOP", desktop_name, 0); - setenv("XDG_SEAT", "seat0", 0); - setenv("XDG_VTNR", tty_id, 0); -} - -void add_utmp_entry( - struct utmp *entry, - char *username, - pid_t display_pid -) { - entry->ut_type = USER_PROCESS; - entry->ut_pid = display_pid; - strcpy(entry->ut_line, ttyname(STDIN_FILENO) + strlen("/dev/")); - - /* only correct for ptys named /dev/tty[pqr][0-9a-z] */ - strcpy(entry->ut_id, ttyname(STDIN_FILENO) + strlen("/dev/tty")); - - time((long int *) &entry->ut_time); - - strncpy(entry->ut_user, username, UT_NAMESIZE); - memset(entry->ut_host, 0, UT_HOSTSIZE); - entry->ut_addr = 0; - setutent(); - - pututline(entry); -} - -void remove_utmp_entry(struct utmp *entry) { - entry->ut_type = DEAD_PROCESS; - memset(entry->ut_line, 0, UT_LINESIZE); - entry->ut_time = 0; - memset(entry->ut_user, 0, UT_NAMESIZE); - setutent(); - pututline(entry); - endutent(); -} - -void xauth(const char* display_name, const char* shell, char* pwd) -{ - const char* xauth_file = "lyxauth"; - char* xauth_dir = getenv("XDG_RUNTIME_DIR"); - if ((xauth_dir == NULL) || (*xauth_dir == '\0')) - { - xauth_dir = getenv("XDG_CONFIG_HOME"); - struct stat sb; - if ((xauth_dir == NULL) || (*xauth_dir == '\0')) - { - xauth_dir = strdup(pwd); - strcat(xauth_dir, "/.config"); - stat(xauth_dir, &sb); - if (S_ISDIR(sb.st_mode)) - { - strcat(xauth_dir, "/ly"); - } - else - { - xauth_dir = pwd; - xauth_file = ".lyxauth"; - } - } - else - { - strcat(xauth_dir, "/ly"); - } - - // If .config/ly/ or XDG_CONFIG_HOME/ly/ doesn't exist and can't create the directory, use pwd - // Passing pwd beforehand is safe since stat will always evaluate false - stat(xauth_dir, &sb); - if (!S_ISDIR(sb.st_mode) && mkdir(xauth_dir, 0777) == -1) - { - xauth_dir = pwd; - xauth_file = ".lyxauth"; - } - } - - // trim trailing slashes - int i = strlen(xauth_dir) - 1; - while (xauth_dir[i] == '/') i--; - xauth_dir[i + 1] = '\0'; - - char xauthority[256]; - snprintf(xauthority, 256, "%s/%s", xauth_dir, xauth_file); - setenv("XAUTHORITY", xauthority, 1); - setenv("DISPLAY", display_name, 1); - - FILE* fp = fopen(xauthority, "ab+"); - - if (fp != NULL) - { - fclose(fp); - } - - pid_t pid = fork(); - - if (pid == 0) - { - char cmd[1024]; - snprintf( - cmd, - 1024, - "%s add %s . `%s`", - config.xauth_cmd, - display_name, - config.mcookie_cmd); - execl(shell, shell, "-c", cmd, NULL); - exit(EXIT_SUCCESS); - } - - int status; - waitpid(pid, &status, 0); -} - -void xorg( - struct passwd* pwd, - const char* vt, - const char* desktop_cmd) -{ - char display_name[4]; - - snprintf(display_name, 3, ":%d", get_free_display()); - xauth(display_name, pwd->pw_shell, pwd->pw_dir); - - // start xorg - pid_t pid = fork(); - - if (pid == 0) - { - char x_cmd[1024]; - snprintf( - x_cmd, - 1024, - "%s %s %s", - config.x_cmd, - display_name, - vt); - execl(pwd->pw_shell, pwd->pw_shell, "-c", x_cmd, NULL); - exit(EXIT_SUCCESS); - } - - int ok; - xcb_connection_t* xcb; - - do - { - xcb = xcb_connect(NULL, NULL); - ok = xcb_connection_has_error(xcb); - kill(pid, 0); - } - while((ok != 0) && (errno != ESRCH)); - - if (ok != 0) - { - return; - } - - pid_t xorg_pid = fork(); - - if (xorg_pid == 0) - { - char de_cmd[1024]; - snprintf( - de_cmd, - 1024, - "%s %s", - config.x_cmd_setup, - desktop_cmd); - execl(pwd->pw_shell, pwd->pw_shell, "-c", de_cmd, NULL); - exit(EXIT_SUCCESS); - } - - int status; - waitpid(xorg_pid, &status, 0); - xcb_disconnect(xcb); - kill(pid, 0); - - if (errno != ESRCH) - { - kill(pid, SIGTERM); - waitpid(pid, &status, 0); - } -} - -void wayland( - struct passwd* pwd, - const char* desktop_cmd) -{ - - char cmd[1024]; - snprintf(cmd, 1024, "%s %s", config.wayland_cmd, desktop_cmd); - execl(pwd->pw_shell, pwd->pw_shell, "-c", cmd, NULL); -} - -void shell(struct passwd* pwd) -{ - const char* pos = strrchr(pwd->pw_shell, '/'); - char args[1024]; - args[0] = '-'; - - if (pos != NULL) - { - pos = pos + 1; - } - else - { - pos = pwd->pw_shell; - } - - strncpy(args + 1, pos, 1023); - execl(pwd->pw_shell, args, NULL); -} - -// pam_do performs the pam action specified in pam_action -// on pam_action fail, call diagnose and end pam session -int pam_do( - int (pam_action)(struct pam_handle *, int), - struct pam_handle *handle, - int flags, - struct term_buf *buf) -{ - int status = pam_action(handle, flags); - - if (status != PAM_SUCCESS) { - pam_diagnose(status, buf); - pam_end(handle, status); - } - - return status; -} - -void auth( - struct desktop* desktop, - struct text* login, - struct text* password, - struct term_buf* buf) -{ - int ok; - - char tty_id [3]; - snprintf(tty_id, 3, "%d", config.tty); - - // Add XDG environment variables - env_xdg_session(desktop->display_server[desktop->cur]); - env_xdg(tty_id, desktop->list_simple[desktop->cur]); - - // open pam session - const char* creds[2] = {login->text, password->text}; - struct pam_conv conv = {login_conv, creds}; - struct pam_handle* handle; - - ok = pam_start(config.service_name, NULL, &conv, &handle); - - if (ok != PAM_SUCCESS) - { - pam_diagnose(ok, buf); - pam_end(handle, ok); - return; - } - - ok = pam_do(pam_authenticate, handle, 0, buf); - - if (ok != PAM_SUCCESS) - { - return; - } - - ok = pam_do(pam_acct_mgmt, handle, 0, buf); - - if (ok != PAM_SUCCESS) - { - return; - } - - ok = pam_do(pam_setcred, handle, PAM_ESTABLISH_CRED, buf); - - if (ok != PAM_SUCCESS) - { - return; - } - - ok = pam_do(pam_open_session, handle, 0, buf); - - if (ok != PAM_SUCCESS) - { - return; - } - - // clear the credentials - input_text_clear(password); - - // get passwd structure - struct passwd* pwd = getpwnam(login->text); - endpwent(); - - if (pwd == NULL) - { - dgn_throw(DGN_PWNAM); - pam_end(handle, ok); - return; - } - - // set user shell - if (pwd->pw_shell[0] == '\0') - { - setusershell(); - - char* shell = getusershell(); - - if (shell != NULL) - { - strcpy(pwd->pw_shell, shell); - } - - endusershell(); - } - - // restore regular terminal mode - tb_clear(); - tb_present(); - tb_shutdown(); - - // start desktop environment - pid_t pid = fork(); - - if (pid == 0) - { - // set user info - ok = initgroups(pwd->pw_name, pwd->pw_gid); - - if (ok != 0) - { - dgn_throw(DGN_USER_INIT); - exit(EXIT_FAILURE); - } - - ok = setgid(pwd->pw_gid); - - if (ok != 0) - { - dgn_throw(DGN_USER_GID); - exit(EXIT_FAILURE); - } - - ok = setuid(pwd->pw_uid); - - if (ok != 0) - { - dgn_throw(DGN_USER_UID); - exit(EXIT_FAILURE); - } - - // get a display - char vt[5]; - snprintf(vt, 5, "vt%d", config.tty); - - // set env (this clears the environment) - env_init(pwd); - // Re-add XDG environment variables from lines 508,509 - env_xdg_session(desktop->display_server[desktop->cur]); - env_xdg(tty_id, desktop->list_simple[desktop->cur]); - - if (dgn_catch()) - { - exit(EXIT_FAILURE); - } - - // add pam variables - char** env = pam_getenvlist(handle); - - for (uint16_t i = 0; env && env[i]; ++i) - { - putenv(env[i]); - } - - // execute - int ok = chdir(pwd->pw_dir); - - if (ok != 0) - { - dgn_throw(DGN_CHDIR); - exit(EXIT_FAILURE); - } - - reset_terminal(pwd); - switch (desktop->display_server[desktop->cur]) - { - case DS_WAYLAND: - { - wayland(pwd, desktop->cmd[desktop->cur]); - break; - } - case DS_SHELL: - { - shell(pwd); - break; - } - case DS_XINITRC: - case DS_XORG: - { - xorg(pwd, vt, desktop->cmd[desktop->cur]); - break; - } - } - - exit(EXIT_SUCCESS); - } - - // add utmp audit - struct utmp entry; - add_utmp_entry(&entry, pwd->pw_name, pid); - - // wait for the session to stop - int status; - waitpid(pid, &status, 0); - remove_utmp_entry(&entry); - - reset_terminal(pwd); - - // reinit termbox - tb_init(); - tb_select_output_mode(TB_OUTPUT_NORMAL); - - // reload the desktop environment list on logout - input_desktop_free(desktop); - input_desktop(desktop); - desktop_load(desktop); - - // close pam session - ok = pam_do(pam_close_session, handle, 0, buf); - - if (ok != PAM_SUCCESS) - { - return; - } - - ok = pam_do(pam_setcred, handle, PAM_DELETE_CRED, buf); - - if (ok != PAM_SUCCESS) - { - return; - } - - ok = pam_end(handle, 0); - - if (ok != PAM_SUCCESS) - { - pam_diagnose(ok, buf); - } -} - diff --git a/src/login.h b/src/login.h deleted file mode 100644 index 3fee58f..0000000 --- a/src/login.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef H_LY_LOGIN -#define H_LY_LOGIN - -#include "draw.h" -#include "inputs.h" - -void auth( - struct desktop* desktop, - struct text* login, - struct text* password, - struct term_buf* buf); - -#endif diff --git a/src/main.zig b/src/main.zig index 1ab8d7b..8fc7d79 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,383 +1,507 @@ const std = @import("std"); -const config = @import("config.zig"); -const utils = @import("utils.zig"); +const build_options = @import("build_options"); +const builtin = @import("builtin"); +const clap = @import("clap"); +const auth = @import("auth.zig"); +const bigclock = @import("bigclock.zig"); +const interop = @import("interop.zig"); +const Doom = @import("animations/Doom.zig"); +const Matrix = @import("animations/Matrix.zig"); +const TerminalBuffer = @import("tui/TerminalBuffer.zig"); +const Desktop = @import("tui/components/Desktop.zig"); +const Text = @import("tui/components/Text.zig"); +const Config = @import("config/Config.zig"); +const ConfigReader = @import("config/ConfigReader.zig"); +const Lang = @import("config/Lang.zig"); + +const termbox = interop.termbox; + +const LY_VERSION = "1.0.0"; -pub const c = @cImport({ - @cInclude("dragonfail.h"); - @cInclude("termbox.h"); +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + + const allocator = gpa.allocator(); + const stderr = std.io.getStdErr().writer(); + + // Load arguments + const params = comptime clap.parseParamsComptime( + \\-h, --help Shows all commands. + \\-v, --version Shows the version of Ly. + \\-c, --config Overrides the default configuration path. Example: --config /usr/share/ly + ); + + var diag = clap.Diagnostic{}; + var res = clap.parse(clap.Help, ¶ms, clap.parsers.default, .{ .diagnostic = &diag }) catch |err| { + diag.report(stderr, err) catch {}; + return err; + }; + defer res.deinit(); + + var config: Config = undefined; + var lang: Lang = undefined; + var info_line: []const u8 = undefined; + + if (res.args.help != 0) { + try clap.help(stderr, clap.Help, ¶ms, .{}); + + _ = try stderr.write("Note: if you want to configure Ly, please check the config file, which is usually located at /etc/ly/config.ini.\n"); + std.os.exit(0); + } + if (res.args.version != 0) { + _ = try stderr.write("Ly version " ++ LY_VERSION ++ "\n"); + std.os.exit(0); + } - @cInclude("draw.h"); - @cInclude("inputs.h"); - @cInclude("login.h"); - @cInclude("utils.h"); - @cInclude("config.h"); -}); + // Load configuration file + var config_reader = ConfigReader.init(allocator); + defer config_reader.deinit(); -// Compile-time settings -const LY_VERSION = "0.7.0"; -const MAX_AUTH_FAILS = 10; + if (res.args.config) |s| { + const trailing_slash = if (s[s.len - 1] != '/') "/" else ""; -// Main allocator for Ly -var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const config_path = try std.fmt.allocPrint(allocator, "{s}{s}config.ini", .{ s, trailing_slash }); + defer allocator.free(config_path); -pub const allocator = gpa.allocator(); + config = try config_reader.readConfig(config_path); -// Ly general and language configuration -pub var c_config: c.struct_config = undefined; -pub var c_lang: c.struct_lang = undefined; + const lang_path = try std.fmt.allocPrint(allocator, "{s}{s}lang/{s}.ini", .{ s, trailing_slash, config.ly.lang }); + defer allocator.free(lang_path); -comptime { - @export(c_config, .{ .name = "config" }); - @export(c_lang, .{ .name = "lang" }); -} + lang = try config_reader.readLang(lang_path); + } else { + config = try config_reader.readConfig(build_options.data_directory ++ "/config.ini"); -// 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); - - c_config = config_ptr.*; - c_lang = lang_ptr.*; - - // Initialize error library - log_init(c.dgn_init()); - - // Parse command line arguments - var config_path: []const u8 = ""; - - var process_args = try std.process.argsAlloc(allocator); - defer std.process.argsFree(allocator, process_args); - - if (process_args.len > 1) { - var first_arg = process_args[1]; - - if (std.mem.eql(u8, first_arg, "--help") or std.mem.eql(u8, first_arg, "-h")) { - std.debug.print("--help (-h) | Shows this help message.\n", .{}); - std.debug.print("--version (-v) | Shows the version of Ly.\n", .{}); - std.debug.print("--config (-c) | Overrides the configuration file path.\n", .{}); - std.debug.print("\n", .{}); - std.debug.print("If you want to configure Ly, please check the config file, usually located at /etc/ly/config.ini.\n", .{}); - std.os.exit(0); - } else if (std.mem.eql(u8, first_arg, "--version") or std.mem.eql(u8, first_arg, "-v")) { - std.debug.print("Ly version {s}.\n", .{LY_VERSION}); - std.os.exit(0); - } else if (std.mem.eql(u8, first_arg, "--config") or std.mem.eql(u8, first_arg, "-c")) { - if (process_args.len != 3) { - std.debug.print("Invalid usage! Correct usage: 'ly --config '.\n", .{}); - std.os.exit(1); - } + const lang_path = try std.fmt.allocPrint(allocator, "{s}/lang/{s}.ini", .{ build_options.data_directory, config.ly.lang }); + defer allocator.free(lang_path); - config_path = process_args[2]; - } else { - std.debug.print("Invalid argument: '{s}'.\n", .{first_arg}); - std.os.exit(1); - } + lang = try config_reader.readLang(lang_path); } - // Load configuration and language - try config.config_load(config_path); - try config.lang_load(); + // Initialize information line with host name + get_host_name: { + const host_name_struct = interop.getHostName(allocator) catch |err| { + if (err == error.CannotGetHostName) { + info_line = lang.ly.err_hostname; + } else { + info_line = lang.ly.err_alloc; + } + break :get_host_name; + }; + defer allocator.free(host_name_struct.buffer); - if (c.dgn_catch() != 0) { - config.config_free(); - config.lang_free(); - std.os.exit(1); + info_line = host_name_struct.slice; } - // Initialize inputs - var desktop = try allocator.create(c.struct_desktop); - defer allocator.destroy(desktop); + // Initialize termbox + _ = termbox.tb_init(); + defer termbox.tb_shutdown(); - var username = try allocator.create(c.struct_text); - defer allocator.destroy(username); + _ = termbox.tb_select_output_mode(termbox.TB_OUTPUT_NORMAL); + termbox.tb_clear(); - var password = try allocator.create(c.struct_text); - defer allocator.destroy(password); + // Initialize terminal buffer + const labels_max_length = @max(lang.ly.login.len, lang.ly.password.len); - c.input_desktop(desktop); - c.input_text(username, config.ly_config.ly.max_login_len); - c.input_text(password, config.ly_config.ly.max_password_len); + var buffer = TerminalBuffer.init(config.ly.margin_box_v, config.ly.margin_box_h, config.ly.input_len, labels_max_length, config.ly.fg, config.ly.bg); - utils.desktop_load(desktop); - try utils.load(desktop, username); + // Initialize components + var desktop = try Desktop.init(allocator, &buffer, config.ly.max_desktop_len); + defer desktop.deinit(); - // Start termbox - _ = c.tb_init(); - _ = c.tb_select_output_mode(c.TB_OUTPUT_NORMAL); - c.tb_clear(); + desktop.addEnvironment(lang.ly.shell, "", .shell) catch { + info_line = lang.ly.err_alloc; + }; + desktop.addEnvironment(lang.ly.xinitrc, config.ly.xinitrc, .xinitrc) catch { + info_line = lang.ly.err_alloc; + }; - // Initialize visible elements - var event = try allocator.create(c.struct_tb_event); - defer allocator.destroy(event); + try desktop.crawl(config.ly.waylandsessions, .wayland); + try desktop.crawl(config.ly.xsessions, .x11); - var buffer = try allocator.create(c.struct_term_buf); - defer allocator.destroy(buffer); + var login = try Text.init(allocator, &buffer, config.ly.max_login_len); + defer login.deinit(); - // 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: config.Inputs = undefined; - if (config.ly_config.ly.default_input == .login and username.text != username.end) { - active_input = .password; - } else { - active_input = config.ly_config.ly.default_input; + var password = try Text.init(allocator, &buffer, config.ly.max_password_len); + defer password.deinit(); + + // Load last saved username and desktop selection, if any + if (config.ly.load) load_last_saved: { + var file = std.fs.openFileAbsolute(config.ly.save_file, .{}) catch break :load_last_saved; + defer file.close(); + + const reader = file.reader(); + const username_length = try reader.readIntLittle(u64); + + const username_buffer = try file.readToEndAlloc(allocator, username_length); + defer allocator.free(username_buffer); + + _ = try reader.read(username_buffer); + + const current_desktop = try reader.readIntLittle(u64); + + const load_buffer = try file.readToEndAlloc(allocator, config.ly.max_login_len + 5); + defer allocator.free(load_buffer); + + if (username_buffer.len > 0) { + try login.text.appendSlice(username_buffer); + login.end = username_buffer.len; + } + + if (current_desktop < desktop.environments.items.len) desktop.current = current_desktop; } - // 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) { - .session => { - c.handle_desktop(desktop, event); - }, - .login => { - c.handle_text(username, event); - }, - .password => { - c.handle_text(password, event); - }, + var active_input = if (config.ly.default_input == .login and login.text.items.len != login.end) .password else config.ly.default_input; + + // Place components on the screen + { + buffer.drawBoxCenter(!config.ly.hide_borders, config.ly.blank_box); + + const coordinates = buffer.calculateComponentCoordinates(); + desktop.position(coordinates.x, coordinates.y + 2, coordinates.visible_length); + login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length); + password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length); + + switch (active_input) { + .session => desktop.handle(null), + .login => login.handle(null) catch { + info_line = lang.ly.err_alloc; + }, + .password => password.handle(null) catch { + info_line = lang.ly.err_alloc; + }, + } } - if (config.ly_config.ly.animate) { - c.animate_init(buffer); + // Initialize the animation, if any + var doom: Doom = undefined; + var matrix: Matrix = undefined; - if (c.dgn_catch() != 0) { - config.ly_config.ly.animate = false; - c.dgn_reset(); + switch (config.ly.animation) { + .none => {}, + .doom => doom = try Doom.init(allocator, &buffer), + .matrix => matrix = try Matrix.init(allocator, &buffer), + } + defer { + switch (config.ly.animation) { + .none => {}, + .doom => doom.deinit(), + .matrix => matrix.deinit(), } } - // Initialize state information - var err: c_int = 0; + const animate = config.ly.animation != .none; + const has_clock = config.ly.clock.len > 0; + const shutdown_key = try std.fmt.parseInt(u8, config.ly.shutdown_key[1..], 10); + const restart_key = try std.fmt.parseInt(u8, config.ly.restart_key[1..], 10); + + var event = std.mem.zeroes(termbox.tb_event); var run = true; var update = true; - var reboot = false; + var resolution_changed = false; var shutdown = false; - var auth_fails: u8 = 0; + var restart = false; + var auth_fails: u64 = 0; + + // Switch to selected TTY if possible + open_console_dev: { + const console_dev_z = allocator.dupeZ(u8, config.ly.console_dev) catch { + info_line = lang.ly.err_alloc; + break :open_console_dev; + }; + defer allocator.free(console_dev_z); + + const fd = std.c.open(console_dev_z, interop.O_WRONLY); + defer _ = std.c.close(fd); + + if (fd < 0) { + info_line = lang.ly.err_console_dev; + break :open_console_dev; + } - c.switch_tty(buffer); + _ = std.c.ioctl(fd, interop.VT_ACTIVATE, config.ly.tty); + _ = std.c.ioctl(fd, interop.VT_WAITACTIVE, config.ly.tty); + } - // Main loop while (run) { + // If there's no input or there's an animation, a resolution change needs to be checked + if (!update or config.ly.animation != .none) { + if (!update) std.time.sleep(100_000_000); + + termbox.tb_present(); // Required to update tb_width(), tb_height() and tb_cell_buffer() + + const width: u64 = @intCast(termbox.tb_width()); + const height: u64 = @intCast(termbox.tb_height()); + + if (width != buffer.width) { + buffer.width = width; + resolution_changed = true; + } + + if (height != buffer.height) { + buffer.height = height; + resolution_changed = true; + } + + // If it did change, then update the cell buffer, reallocate the current animation's buffers, and force a draw update + if (resolution_changed) { + buffer.buffer = termbox.tb_cell_buffer(); + + switch (config.ly.animation) { + .none => {}, + .doom => doom.realloc() catch { + info_line = lang.ly.err_alloc; + }, + .matrix => matrix.realloc() catch { + info_line = lang.ly.err_alloc; + }, + } + + update = true; + } + } + if (update) { - if (auth_fails < MAX_AUTH_FAILS) { + // If the user entered a wrong password 10 times in a row, play a cascade animation, else update normally + if (auth_fails < 10) { switch (active_input) { - .session => { - c.handle_desktop(desktop, event); - }, - .login => { - c.handle_text(username, event); + .session => desktop.handle(null), + .login => login.handle(null) catch { + info_line = lang.ly.err_alloc; }, - .password => { - c.handle_text(password, event); + .password => password.handle(null) catch { + info_line = lang.ly.err_alloc; }, } - c.tb_clear(); - c.animate(buffer); - c.draw_bigclock(buffer); - c.draw_box(buffer); - c.draw_clock(buffer); - c.draw_labels(buffer); - if (!config.ly_config.ly.hide_f1_commands) { - c.draw_f_commands(); + termbox.tb_clear(); + + switch (config.ly.animation) { + .none => {}, + .doom => doom.draw(), + .matrix => matrix.draw(), } - 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.ly_config.ly.animate; - } else { - std.time.sleep(10000000); // Sleep 0.01 seconds - update = c.cascade(buffer, &auth_fails); - } - c.tb_present(); - } + if (config.ly.bigclock and buffer.box_height + (bigclock.HEIGHT + 2) * 2 < buffer.height) draw_big_clock: { + const format = "%H:%M"; + const xo = buffer.width / 2 - (format.len * (bigclock.WIDTH + 1)) / 2; + const yo = (buffer.height - buffer.box_height) / 2 - bigclock.HEIGHT - 2; - var timeout: c_int = -1; + const clock_str = interop.timeAsString(allocator, format, format.len + 1) catch { + info_line = lang.ly.err_alloc; + break :draw_big_clock; + }; + defer allocator.free(clock_str); - if (config.ly_config.ly.animate) { - timeout = config.ly_config.ly.min_refresh_delta; - } else { - // TODO: Use the Zig standard library directly - var time = try allocator.create(std.os.linux.timeval); - defer allocator.destroy(time); + for (0..format.len) |i| { + const clock_cell = bigclock.clockCell(animate, clock_str[i], buffer.fg, buffer.bg); + bigclock.alphaBlit(buffer.buffer, xo + i * (bigclock.WIDTH + 1), yo, buffer.width, buffer.height, clock_cell); + } + } - _ = std.os.linux.gettimeofday(time, undefined); + buffer.drawBoxCenter(!config.ly.hide_borders, config.ly.blank_box); - if (config.ly_config.ly.bigclock) { - timeout = @intCast((60 - @mod(time.tv_sec, 60)) * 1000 - @divTrunc(time.tv_usec, 1000) + 1); - } else if (config.ly_config.ly.clock.len > 0) { - timeout = @intCast(1000 - @divTrunc(time.tv_usec, 1000) + 1); - } - } + if (has_clock) draw_clock: { + const clock_buffer = interop.timeAsString(allocator, config.ly.clock, 32) catch { + info_line = lang.ly.err_alloc; + break :draw_clock; + }; + defer allocator.free(clock_buffer); - if (timeout == -1) { - err = c.tb_poll_event(event); - } else { - err = c.tb_peek_event(event, timeout); - } + var clock_str_length: u64 = 0; + for (clock_buffer, 0..) |char, i| { + if (char == 0) { + clock_str_length = i; + break; + } + } - if (err < 0) { - continue; - } + if (clock_str_length == 0) return error.FormattedTimeEmpty; - 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 != .session) { - switch (active_input) { - .login => { - c.input_text_clear(username); - }, - .password => { - c.input_text_clear(password); - }, - else => unreachable, - } + buffer.drawLabel(clock_buffer[0..clock_str_length], buffer.width - clock_str_length, 0); + } - update = true; - } - }, - c.TB_KEY_CTRL_K, c.TB_KEY_ARROW_UP => { - if (active_input != .session) { - active_input = switch (active_input) { - .login => .session, - .password => .login, - else => unreachable, - }; - - update = true; - } - }, - c.TB_KEY_CTRL_J, c.TB_KEY_ARROW_DOWN => { - if (active_input != .password) { - active_input = switch (active_input) { - .session => .login, - .login => .password, - else => unreachable, - }; - - update = true; - } - }, - c.TB_KEY_TAB => { - active_input = switch (active_input) { - .session => .login, - .login => .password, - .password => .session, - }; + const label_x = buffer.box_x + buffer.margin_box_h; + const label_y = buffer.box_y + buffer.margin_box_v; - update = true; - }, - c.TB_KEY_ENTER => { - try utils.save(desktop, username); - c.auth(desktop, username, password, buffer); + buffer.drawLabel(lang.ly.login, label_x, label_y + 4); + buffer.drawLabel(lang.ly.password, label_x, label_y + 6); - if (c.dgn_catch() != 0) { - auth_fails += 1; + if (info_line.len > 0) { + const x = buffer.box_x + ((buffer.box_width - info_line.len) / 2); + buffer.drawLabel(info_line, x, label_y); + } - // Move focus back to password input - active_input = .password; + if (!config.ly.hide_key_hints) { + var length: u64 = 0; - if (c.dgn_output_code() != c.DGN_PAM) { - buffer.info_line = c.dgn_output_log(); - } + buffer.drawLabel(config.ly.shutdown_key, length, 0); + length += config.ly.shutdown_key.len + 1; - if (config.ly_config.ly.blank_password) { - c.input_text_clear(password); + buffer.drawLabel(lang.ly.shutdown, length, 0); + length += lang.ly.shutdown.len + 1; + + buffer.drawLabel(config.ly.restart_key, length, 0); + length += config.ly.restart_key.len + 1; + + buffer.drawLabel(lang.ly.restart, length, 0); + length += lang.ly.restart.len + 1; + } + + draw_lock_state: { + const lock_state = interop.getLockState(allocator, config.ly.console_dev) catch |err| { + if (err == error.CannotOpenConsoleDev) { + info_line = lang.ly.err_console_dev; + } else { + info_line = lang.ly.err_alloc; } + break :draw_lock_state; + }; - c.dgn_reset(); - } else { - buffer.info_line = c_lang.logout; - } + var lock_state_x = buffer.width - lang.ly.numlock.len; + const lock_state_y: u64 = if (has_clock) 1 else 0; - try utils.load(desktop, username); + if (lock_state.numlock) buffer.drawLabel(lang.ly.numlock, lock_state_x, lock_state_y); + lock_state_x -= lang.ly.capslock.len + 1; + if (lock_state.capslock) buffer.drawLabel(lang.ly.capslock, lock_state_x, lock_state_y); + } - // Reset cursor to its normal state - _ = std.ChildProcess.exec(.{ .argv = &[_][]const u8{ "/usr/bin/tput", "cnorm" }, .allocator = allocator }) catch return; + if (resolution_changed) { + const coordinates = buffer.calculateComponentCoordinates(); + desktop.position(coordinates.x, coordinates.y + 2, coordinates.visible_length); + login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length); + password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length); - update = true; - }, - else => { - update = true; - }, + resolution_changed = false; + } + + desktop.draw(); + login.draw(); + password.drawMasked(config.ly.asterisk); + + update = animate; + } else { + std.time.sleep(10_000_000); + update = buffer.cascade(); + + if (!update) { + std.time.sleep(7_000_000_000); + auth_fails = 0; + } } + + termbox.tb_present(); } - } - // Stop termbox - c.tb_shutdown(); + var timeout: i32 = -1; - // Free inputs - c.input_desktop_free(desktop); - c.input_text_free(username); - c.input_text_free(password); - c.free_hostname(); + // Calculate the maximum timeout based on current animations, or the (big) clock. If there's none, we wait for the event indefinitely instead + if (animate) { + timeout = config.ly.min_refresh_delta; + } else if (config.ly.bigclock and config.ly.clock.len == 0) { + var tv = std.mem.zeroes(std.c.timeval); + _ = std.c.gettimeofday(&tv, null); - // Unload configuration - c.draw_free(buffer); - config.lang_free(); + timeout = @intCast((60 - @rem(tv.tv_sec, 60)) * 1000 - @divTrunc(tv.tv_usec, 1000) + 1); + } else if (config.ly.clock.len > 0 or auth_fails >= 10) { + var tv = std.mem.zeroes(std.c.timeval); + _ = std.c.gettimeofday(&tv, null); - if (shutdown) { - var shutdown_cmd = try std.fmt.allocPrint(allocator, "{s}", .{config.ly_config.ly.shutdown_cmd}); - // This will never be freed! But it's fine, we're shutting down the system anyway - defer allocator.free(shutdown_cmd); + timeout = @intCast(1000 - @divTrunc(tv.tv_usec, 1000) + 1); + } - config.config_free(); + const event_error = if (timeout == -1) termbox.tb_poll_event(&event) else termbox.tb_peek_event(&event, timeout); - 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.ly_config.ly.restart_cmd}); - // This will never be freed! But it's fine, we're rebooting the system anyway - defer allocator.free(restart_cmd); + if (event_error < 0 or event.type != termbox.TB_EVENT_KEY) continue; - config.config_free(); + switch (event.key) { + termbox.TB_KEY_F1, termbox.TB_KEY_F2, termbox.TB_KEY_F3, termbox.TB_KEY_F4, termbox.TB_KEY_F5, termbox.TB_KEY_F6, termbox.TB_KEY_F7, termbox.TB_KEY_F8, termbox.TB_KEY_F9, termbox.TB_KEY_F10, termbox.TB_KEY_F11, termbox.TB_KEY_F12 => { + if (0xFFFF - event.key + 1 == shutdown_key) { + shutdown = true; + run = false; + } else if (0xFFFF - event.key + 1 == restart_key) { + restart = true; + run = false; + } + }, + termbox.TB_KEY_CTRL_C => run = false, + termbox.TB_KEY_CTRL_U => { + if (active_input == .login) { + login.clear(); + update = true; + } else if (active_input == .password) { + password.clear(); + update = true; + } + }, + termbox.TB_KEY_CTRL_K, termbox.TB_KEY_ARROW_UP => { + active_input = switch (active_input) { + .session, .login => .session, + .password => .login, + }; + update = true; + }, + termbox.TB_KEY_CTRL_J, termbox.TB_KEY_ARROW_DOWN => { + active_input = switch (active_input) { + .session => .login, + .login, .password => .password, + }; + update = true; + }, + termbox.TB_KEY_TAB => { + active_input = switch (active_input) { + .session => .login, + .login => .password, + .password => .session, + }; + update = true; + }, + termbox.TB_KEY_ENTER => authenticate: { + if (config.ly.save) save_last_settings: { + var file = std.fs.createFileAbsolute(config.ly.save_file, .{}) catch break :save_last_settings; + defer file.close(); + + const writer = file.writer(); + try writer.writeIntLittle(u64, login.end); + _ = try writer.write(login.text.items); + try writer.writeIntLittle(u64, desktop.current); + } - std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", restart_cmd }) catch return; - } else { - config.config_free(); + var has_error = false; + + auth.authenticate(allocator, config.ly.tty, buffer, desktop, login, password) catch { + has_error = true; + auth_fails += 1; + active_input = .password; + + // TODO: Errors in info_line + + if (config.ly.blank_password) password.clear(); + }; + update = true; + + if (!has_error) info_line = lang.ly.logout; + std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.ly.term_restore_cursor_cmd }) catch break :authenticate; + }, + else => { + switch (active_input) { + .session => desktop.handle(&event), + .login => login.handle(&event) catch { + info_line = lang.ly.err_alloc; + }, + .password => password.handle(&event) catch { + info_line = lang.ly.err_alloc; + }, + } + update = true; + }, + } } -} -// Low-level error messages -fn log_init(log: [*c][*c]u8) void { - log[c.DGN_OK] = c_lang.err_dgn_oob; - log[c.DGN_NULL] = c_lang.err_null; - log[c.DGN_ALLOC] = c_lang.err_alloc; - log[c.DGN_BOUNDS] = c_lang.err_bounds; - log[c.DGN_DOMAIN] = c_lang.err_domain; - log[c.DGN_MLOCK] = c_lang.err_mlock; - log[c.DGN_XSESSIONS_DIR] = c_lang.err_xsessions_dir; - log[c.DGN_XSESSIONS_OPEN] = c_lang.err_xsessions_open; - log[c.DGN_PATH] = c_lang.err_path; - log[c.DGN_CHDIR] = c_lang.err_chdir; - log[c.DGN_PWNAM] = c_lang.err_pwnam; - log[c.DGN_USER_INIT] = c_lang.err_user_init; - log[c.DGN_USER_GID] = c_lang.err_user_gid; - log[c.DGN_USER_UID] = c_lang.err_user_uid; - log[c.DGN_PAM] = c_lang.err_pam; - log[c.DGN_HOSTNAME] = c_lang.err_hostname; + if (shutdown) { + return std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.ly.shutdown_cmd }); + } else if (restart) { + return std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.ly.restart_cmd }); + } } diff --git a/src/tui/TerminalBuffer.zig b/src/tui/TerminalBuffer.zig new file mode 100644 index 0000000..e586a23 --- /dev/null +++ b/src/tui/TerminalBuffer.zig @@ -0,0 +1,169 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const interop = @import("../interop.zig"); +const utils = @import("utils.zig"); + +const Allocator = std.mem.Allocator; +const Random = std.rand.Random; + +const termbox = interop.termbox; + +const TerminalBuffer = @This(); + +random: Random, +width: u64, +height: u64, +buffer: [*]termbox.tb_cell, +fg: u8, +bg: u8, +box_chars: struct { + left_up: u32, + left_down: u32, + right_up: u32, + right_down: u32, + top: u32, + bottom: u32, + left: u32, + right: u32, +}, +labels_max_length: u64, +box_x: u64, +box_y: u64, +box_width: u64, +box_height: u64, +margin_box_v: u8, +margin_box_h: u8, + +pub fn init(margin_box_v: u8, margin_box_h: u8, input_length: u8, labels_max_length: u64, fg: u8, bg: u8) TerminalBuffer { + var prng = std.rand.Isaac64.init(@intCast(std.time.timestamp())); + + return .{ + .random = prng.random(), + .width = @intCast(termbox.tb_width()), + .height = @intCast(termbox.tb_height()), + .buffer = termbox.tb_cell_buffer(), + .fg = fg, + .bg = bg, + .box_chars = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) .{ + .left_up = 0x250C, + .left_down = 0x2514, + .right_up = 0x2510, + .right_down = 0x2518, + .top = 0x2500, + .bottom = 0x2500, + .left = 0x2502, + .right = 0x2502, + } else .{ + .left_up = '+', + .left_down = '+', + .right_up = '+', + .right_down = '+', + .top = '-', + .bottom = '-', + .left = '|', + .right = '|', + }, + .labels_max_length = labels_max_length, + .box_x = 0, + .box_y = 0, + .box_width = (2 * margin_box_h) + input_length + 1 + labels_max_length, + .box_height = 7 + (2 * margin_box_v), + .margin_box_v = margin_box_v, + .margin_box_h = margin_box_h, + }; +} + +pub fn cascade(self: TerminalBuffer) bool { + var changes = false; + + var y = self.height - 2; + while (y > 0) : (y -= 1) { + for (0..self.width) |x| { + const c: u8 = @truncate(self.buffer[(y - 1) * self.width + x].ch); + if (std.ascii.isWhitespace(c)) continue; + + const c_under: u8 = @truncate(self.buffer[y * self.width + x].ch); + if (!std.ascii.isWhitespace(c_under)) continue; + + changes = true; + + if ((self.random.int(u16) % 10) > 7) continue; + + self.buffer[y * self.width + x] = self.buffer[(y - 1) * self.width + x]; + self.buffer[(y - 1) * self.width + x].ch = ' '; + } + } + + return changes; +} + +pub fn drawBoxCenter(self: *TerminalBuffer, show_borders: bool, blank_box: bool) void { + const x1 = (self.width - self.box_width) / 2; + const y1 = (self.height - self.box_height) / 2; + const x2 = (self.width + self.box_width) / 2; + const y2 = (self.height + self.box_height) / 2; + + self.box_x = x1; + self.box_y = y1; + + if (show_borders) { + termbox.tb_change_cell(@intCast(x1 - 1), @intCast(y1 - 1), self.box_chars.left_up, self.fg, self.bg); + termbox.tb_change_cell(@intCast(x2), @intCast(y1 - 1), self.box_chars.right_up, self.fg, self.bg); + termbox.tb_change_cell(@intCast(x1 - 1), @intCast(y2), self.box_chars.left_down, self.fg, self.bg); + termbox.tb_change_cell(@intCast(x2), @intCast(y2), self.box_chars.right_down, self.fg, self.bg); + + var c1 = utils.initCell(self.box_chars.top, self.fg, self.bg); + var c2 = utils.initCell(self.box_chars.bottom, self.fg, self.bg); + + for (0..self.box_width) |i| { + termbox.tb_put_cell(@intCast(x1 + i), @intCast(y1 - 1), &c1); + termbox.tb_put_cell(@intCast(x1 + i), @intCast(y2), &c2); + } + + c1.ch = self.box_chars.left; + c2.ch = self.box_chars.right; + + for (0..self.box_height) |i| { + termbox.tb_put_cell(@intCast(x1 - 1), @intCast(y1 + i), &c1); + termbox.tb_put_cell(@intCast(x2), @intCast(y1 + i), &c2); + } + } + + if (blank_box) { + const blank = utils.initCell(' ', self.fg, self.bg); + + for (0..self.box_height) |y| { + for (0..self.box_width) |x| { + termbox.tb_put_cell(@intCast(x1 + x), @intCast(y1 + y), &blank); + } + } + } +} + +pub fn calculateComponentCoordinates(self: TerminalBuffer) struct { + x: u64, + y: u64, + visible_length: u64, +} { + const x = self.box_x + self.margin_box_h + self.labels_max_length + 1; + const y = self.box_y + self.margin_box_v; + const visible_length = self.box_x + self.box_width - self.margin_box_h - x; + + return .{ + .x = x, + .y = y, + .visible_length = visible_length, + }; +} + +pub fn drawLabel(self: TerminalBuffer, text: []const u8, x: u64, y: u64) void { + const yc: c_int = @intCast(y); + for (0..text.len) |xx| termbox.tb_change_cell(@intCast(x + xx), yc, text[xx], self.fg, self.bg); +} + +pub fn drawCharMultiple(self: TerminalBuffer, char: u8, x: u64, y: u64, length: u64) void { + const yc: c_int = @intCast(y); + const cell = utils.initCell(char, self.fg, self.bg); + + for (0..length) |xx| termbox.tb_put_cell(@intCast(x + xx), yc, &cell); +} diff --git a/src/tui/components/Desktop.zig b/src/tui/components/Desktop.zig new file mode 100644 index 0000000..ac5604e --- /dev/null +++ b/src/tui/components/Desktop.zig @@ -0,0 +1,170 @@ +const std = @import("std"); +const ini = @import("ini"); +const enums = @import("../../enums.zig"); +const interop = @import("../../interop.zig"); +const TerminalBuffer = @import("../TerminalBuffer.zig"); + +const Allocator = std.mem.Allocator; +const EnvironmentList = std.ArrayList(Environment); + +const DisplayServer = enums.DisplayServer; + +const termbox = interop.termbox; + +pub const DESKTOP_ENTRY_MAX_SIZE = 8 * 1024; + +const Desktop = @This(); + +pub const Environment = struct { + has_entry_buffer: bool, + entry_buffer: []u8, + name: []const u8, + xdg_name: []const u8, + cmd: []const u8, + specifier: []const u8, + display_server: DisplayServer, +}; + +pub const Entry = struct { + Desktop_Entry: struct { + Exec: []const u8, + Name: []const u8, + }, +}; + +allocator: Allocator, +buffer: *TerminalBuffer, +environments: EnvironmentList, +current: u64, +visible_length: u64, +x: u64, +y: u64, + +pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: u64) !Desktop { + return .{ + .allocator = allocator, + .buffer = buffer, + .environments = try EnvironmentList.initCapacity(allocator, max_length), + .current = 0, + .visible_length = 0, + .x = 0, + .y = 0, + }; +} + +pub fn deinit(self: Desktop) void { + for (self.environments.items) |environment| { + if (environment.has_entry_buffer) self.allocator.free(environment.entry_buffer); + } + + self.environments.deinit(); +} + +pub fn position(self: *Desktop, x: u64, y: u64, visible_length: u64) void { + self.x = x; + self.y = y; + self.visible_length = visible_length; +} + +pub fn addEnvironment(self: *Desktop, name: []const u8, cmd: []const u8, display_server: DisplayServer) !void { + try self.environments.append(.{ + .has_entry_buffer = false, + .entry_buffer = undefined, + .name = name, + .xdg_name = name, // TODO + .cmd = cmd, + .specifier = switch (display_server) { + .wayland => "wayland", + .x11 => "x11", + else => "other", + }, + .display_server = display_server, + }); + + self.current = self.environments.items.len - 1; +} + +pub fn addEnvironmentWithBuffer(self: *Desktop, entry_buffer: []u8, name: []const u8, cmd: []const u8, display_server: DisplayServer) !void { + try self.environments.append(.{ + .has_entry_buffer = true, + .entry_buffer = entry_buffer, + .name = name, + .xdg_name = name, // TODO + .cmd = cmd, + .specifier = switch (display_server) { + .wayland => "wayland", + .x11 => "x11", + else => "other", + }, + .display_server = display_server, + }); + + self.current = self.environments.items.len - 1; +} + +pub fn crawl(self: *Desktop, path: []const u8, display_server: DisplayServer) !void { + var directory = try std.fs.openDirAbsolute(path, .{}); + defer directory.close(); + + var iterable_directory = try std.fs.openIterableDirAbsolute(path, .{}); + defer iterable_directory.close(); + + var iterator = iterable_directory.iterate(); + while (try iterator.next()) |item| { + var file = try directory.openFile(item.name, .{}); + defer file.close(); + + const buffer = try file.readToEndAlloc(self.allocator, DESKTOP_ENTRY_MAX_SIZE); + const entry = try ini.readToStruct(Entry, buffer); + + try self.addEnvironmentWithBuffer(buffer, entry.Desktop_Entry.Name, entry.Desktop_Entry.Exec, display_server); + } +} + +pub fn handle(self: *Desktop, maybe_event: ?*termbox.tb_event) void { + if (maybe_event) |event| blk: { + if (event.type != termbox.TB_EVENT_KEY) break :blk; + + switch (event.key) { + termbox.TB_KEY_ARROW_LEFT, termbox.TB_KEY_CTRL_H => self.goLeft(), + termbox.TB_KEY_ARROW_RIGHT, termbox.TB_KEY_CTRL_L => self.goRight(), + else => {}, + } + } + + termbox.tb_set_cursor(@intCast(self.x + 2), @intCast(self.y)); +} + +pub fn draw(self: Desktop) void { + const environment = self.environments.items[self.current]; + + const length = @min(environment.name.len, self.visible_length - 3); + if (length == 0) return; + + const x = self.buffer.box_x + self.buffer.margin_box_h; + const y = self.buffer.box_y + self.buffer.margin_box_v + 2; + self.buffer.drawLabel(environment.specifier, x, y); + + termbox.tb_change_cell(@intCast(self.x), @intCast(self.y), '<', self.buffer.fg, self.buffer.bg); + termbox.tb_change_cell(@intCast(self.x + self.visible_length - 1), @intCast(self.y), '>', self.buffer.fg, self.buffer.bg); + + self.buffer.drawLabel(environment.name, self.x + 2, self.y); +} + +fn goLeft(self: *Desktop) void { + if (self.current == 0) { + self.current = self.environments.items.len - 1; + return; + } + + self.current -= 1; +} + +fn goRight(self: *Desktop) void { + if (self.current == self.environments.items.len - 1) { + self.current = 0; + return; + } + + self.current += 1; +} diff --git a/src/tui/components/Text.zig b/src/tui/components/Text.zig new file mode 100644 index 0000000..304f37b --- /dev/null +++ b/src/tui/components/Text.zig @@ -0,0 +1,122 @@ +const std = @import("std"); +const interop = @import("../../interop.zig"); +const TerminalBuffer = @import("../TerminalBuffer.zig"); +const utils = @import("../utils.zig"); + +const Allocator = std.mem.Allocator; +const DynamicString = std.ArrayList(u8); + +const termbox = interop.termbox; + +const Text = @This(); + +allocator: Allocator, +buffer: *TerminalBuffer, +text: DynamicString, +end: u64, +cursor: u64, +visible_start: u64, +visible_length: u64, +x: u64, +y: u64, + +pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: u64) !Text { + const text = try DynamicString.initCapacity(allocator, max_length); + + return .{ + .allocator = allocator, + .buffer = buffer, + .text = text, + .end = 0, + .cursor = 0, + .visible_start = 0, + .visible_length = 0, + .x = 0, + .y = 0, + }; +} + +pub fn deinit(self: Text) void { + self.text.deinit(); +} + +pub fn position(self: *Text, x: u64, y: u64, visible_length: u64) void { + self.x = x; + self.y = y; + self.visible_length = visible_length; +} + +pub fn handle(self: *Text, maybe_event: ?*termbox.tb_event) !void { + if (maybe_event) |event| blk: { + if (event.type != termbox.TB_EVENT_KEY) break :blk; + + switch (event.key) { + termbox.TB_KEY_ARROW_LEFT => self.goLeft(), + termbox.TB_KEY_ARROW_RIGHT => self.goRight(), + termbox.TB_KEY_DELETE => self.delete(), + termbox.TB_KEY_BACKSPACE, termbox.TB_KEY_BACKSPACE2 => self.backspace(), + termbox.TB_KEY_SPACE => try self.write(' '), + else => if (event.ch > 31 and event.ch < 127) try self.write(@intCast(event.ch)), + } + } + + termbox.tb_set_cursor(@intCast(self.x + (self.cursor - self.visible_start)), @intCast(self.y)); +} + +pub fn draw(self: Text) void { + const length = @min(self.text.items.len, self.visible_length); + if (length == 0) return; + + const visible_slice = if (self.text.items.len > self.visible_length and self.cursor < self.text.items.len) self.text.items[self.visible_start..(self.visible_length + self.visible_start)] else self.text.items[self.visible_start..]; + self.buffer.drawLabel(visible_slice, self.x, self.y); +} + +pub fn drawMasked(self: Text, mask: u8) void { + const length = @min(self.text.items.len, self.visible_length - 1); + if (length == 0) return; + + self.buffer.drawCharMultiple(mask, self.x, self.y, length); +} + +pub fn clear(self: *Text) void { + self.text.clearRetainingCapacity(); + self.end = 0; + self.cursor = 0; + self.visible_start = 0; +} + +fn goLeft(self: *Text) void { + if (self.cursor == 0) return; + if (self.visible_start > 0) self.visible_start -= 1; + + self.cursor -= 1; +} + +fn goRight(self: *Text) void { + if (self.cursor >= self.end) return; + if (self.cursor - self.visible_start == self.visible_length - 1) self.visible_start += 1; + + self.cursor += 1; +} + +fn delete(self: *Text) void { + _ = self.text.orderedRemove(self.cursor); + + self.end -= 1; +} + +fn backspace(self: *Text) void { + if (self.cursor == 0) return; + + self.goLeft(); + self.delete(); +} + +fn write(self: *Text, char: u8) !void { + if (char == 0) return; + + try self.text.insert(self.cursor, char); + + self.end += 1; + self.goRight(); +} diff --git a/src/tui/utils.zig b/src/tui/utils.zig new file mode 100644 index 0000000..bf9304e --- /dev/null +++ b/src/tui/utils.zig @@ -0,0 +1,12 @@ +const std = @import("std"); +const interop = @import("../interop.zig"); + +const termbox = interop.termbox; + +pub fn initCell(ch: u32, fg: u32, bg: u32) termbox.tb_cell { + var cell = std.mem.zeroes(termbox.tb_cell); + cell.ch = ch; + cell.fg = fg; + cell.bg = bg; + return cell; +} diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index 2c08de9..0000000 --- a/src/utils.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "configator.h" -#include "dragonfail.h" -#include "inputs.h" -#include "config.h" -#include "utils.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(__DragonFly__) || defined(__FreeBSD__) - #include -#else // linux - #include -#endif - -static char* hostname_backup = NULL; - -void hostname(char** out) -{ - if (hostname_backup != NULL) - { - *out = hostname_backup; - return; - } - - int maxlen = sysconf(_SC_HOST_NAME_MAX); - - if (maxlen < 0) - { - maxlen = _POSIX_HOST_NAME_MAX; - } - - hostname_backup = malloc(maxlen + 1); - - if (hostname_backup == NULL) - { - dgn_throw(DGN_ALLOC); - return; - } - - if (gethostname(hostname_backup, maxlen) < 0) - { - dgn_throw(DGN_HOSTNAME); - return; - } - - hostname_backup[maxlen] = '\0'; - *out = hostname_backup; -} - -void free_hostname() -{ - free(hostname_backup); -} - -void switch_tty(struct term_buf* buf) -{ - FILE* console = fopen(config.console_dev, "w"); - - if (console == NULL) - { - buf->info_line = lang.err_console_dev; - return; - } - - int fd = fileno(console); - - ioctl(fd, VT_ACTIVATE, config.tty); - ioctl(fd, VT_WAITACTIVE, config.tty); - - fclose(console); -} \ No newline at end of file diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index c5b9028..0000000 --- a/src/utils.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef H_LY_UTILS -#define H_LY_UTILS - -#include "draw.h" -#include "inputs.h" -#include "config.h" - -void desktop_load(struct desktop* target); -void hostname(char** out); -void free_hostname(); -void switch_tty(struct term_buf* buf); - -#endif diff --git a/src/utils.zig b/src/utils.zig deleted file mode 100644 index 5cf1891..0000000 --- a/src/utils.zig +++ /dev/null @@ -1,114 +0,0 @@ -const std = @import("std"); -const ini = @import("ini"); -const main = @import("main.zig"); -const config = @import("config.zig"); -const interop = @import("interop.zig"); - -const DESKTOP_ENTRY_MAX_SIZE: usize = 8 * 1024; - -const Entry = struct { - Desktop_Entry: struct { - Name: []const u8, - Comment: []const u8, - Exec: []const u8, - Type: []const u8, - DesktopNames: []const u8, - }, -}; - -pub export fn desktop_load(target: *main.c.struct_desktop) void { - // We don't care about desktop environments presence - // because the fallback shell is always available - // so we just dismiss any "throw" for now - var err: c_int = 0; - - desktop_crawl(target, config.ly_config.ly.waylandsessions, main.c.DS_WAYLAND) catch {}; - - if (main.c.dgn_catch() != 0) { - err += 1; - main.c.dgn_reset(); - } - - desktop_crawl(target, config.ly_config.ly.xsessions, main.c.DS_XORG) catch {}; - - if (main.c.dgn_catch() != 0) { - err += 1; - main.c.dgn_reset(); - } -} - -pub fn save(desktop: *main.c.struct_desktop, login: *main.c.struct_text) !void { - if (!config.ly_config.ly.save) { - return; - } - - var file = std.fs.openFileAbsolute(config.ly_config.ly.save_file, .{ .mode = .write_only }) catch { - return; - }; - defer file.close(); - - var buffer = try std.fmt.allocPrint(main.allocator, "{s}\n{d}", .{ login.*.text, desktop.*.cur }); - defer main.allocator.free(buffer); - - try file.writeAll(buffer); -} - -pub fn load(desktop: *main.c.struct_desktop, login: *main.c.struct_text) !void { - if (!config.ly_config.ly.load) { - return; - } - - var file = std.fs.openFileAbsolute(config.ly_config.ly.save_file, .{}) catch { - return; - }; - defer file.close(); - - var buffer = try main.allocator.alloc(u8, config.ly_config.ly.max_login_len * 2 + 1); - defer main.allocator.free(buffer); - - _ = try file.readAll(buffer); - - var array = std.mem.splitSequence(u8, buffer, "\n"); - - login.*.text = try interop.c_str(array.first()); // TODO: Free? - desktop.*.cur = try std.fmt.parseUnsigned(u16, array.next().?, 0); -} - -fn desktop_crawl(target: *main.c.struct_desktop, sessions: []const u8, server: main.c.enum_display_server) !void { - var iterable_dir = std.fs.openIterableDirAbsolute(sessions, .{}) catch { - main.c.dgn_throw(main.c.DGN_XSESSIONS_OPEN); - return; - }; - defer iterable_dir.close(); - - var iterator = iterable_dir.iterate(); - - var dir = std.fs.openDirAbsolute(sessions, .{}) catch { - main.c.dgn_throw(main.c.DGN_XSESSIONS_OPEN); - return; - }; - defer dir.close(); - - while (try iterator.next()) |item| { - if (!std.mem.endsWith(u8, item.name, ".desktop")) { - continue; - } - - var file = try dir.openFile(item.name, .{}); - defer file.close(); - - var buffer = try main.allocator.alloc(u8, DESKTOP_ENTRY_MAX_SIZE); // TODO: Free - var length = try file.readAll(buffer); - var entry = try ini.readToStruct(Entry, buffer[0..length]); - - // TODO: If it's a wayland session, add " (Wayland)" to its name, - // as long as it doesn't already contain that string - - const name = entry.Desktop_Entry.Name; - const exec = entry.Desktop_Entry.Exec; - - if (name.len > 0 and exec.len > 0) { - main.c.input_desktop_add(target, (try interop.c_str(name)).ptr, (try interop.c_str(exec)).ptr, server); - } - } -}