From 6b2d76c9b8af8d3c10ea5ba7d9a6e334555806e0 Mon Sep 17 00:00:00 2001 From: javalsai Date: Sat, 13 Jul 2024 01:30:47 +0200 Subject: [PATCH] =?UTF-8?q?all:=20at=20this=20point=20just=20read=20readme?= =?UTF-8?q?=20=F0=9F=98=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spaghetti code equivalent spaghetti readme spaghetti commit (wait no, this is actually descriptive) --- Makefile | 6 +- README.md | 72 +++++++ assets/lidm.png | Bin 0 -> 14056 bytes compile_commands.json | 38 +++- include/efield.h | 18 ++ include/keys.h | 104 +++++++++++ include/sessions.h | 13 +- include/ui.h | 80 ++++++++ include/users.h | 6 + include/util.h | 11 ++ src/efield.c | 64 +++++++ src/main.c | 84 +++++++-- src/sessions.c | 20 +- src/ui.c | 425 ++++++++++++++++++++++++++++++++++++++++++ src/users.c | 3 +- src/util.c | 49 +++++ 16 files changed, 954 insertions(+), 39 deletions(-) create mode 100644 README.md create mode 100644 assets/lidm.png create mode 100644 include/efield.h create mode 100644 include/keys.h create mode 100644 include/ui.h create mode 100644 src/efield.c create mode 100644 src/ui.c diff --git a/Makefile b/Makefile index f2e90a4..3e9ed7b 100644 --- a/Makefile +++ b/Makefile @@ -8,17 +8,17 @@ CFLAGS=-I$(IDIR) LIBS=-lm -_DEPS = util.h users.h sessions.h +_DEPS = util.h ui.h efield.h keys.h users.h sessions.h DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS)) -_OBJ = main.o util.o users.o sessions.o +_OBJ = main.o util.o ui.o efield.o users.o sessions.o OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ)) $(ODIR)/%.o: $(CDIR)/%.c $(DEPS) @mkdir -p $(ODIR) $(CC) -c -o $@ $< $(CFLAGS) -li: $(OBJ) +lidm: $(OBJ) $(CC) -o $@ $^ $(CFLAGS) $(LIBS) .PHONY: clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..f858786 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# lidm +Lidm is a really light display manager made in C, highly customizable and held together by hopes and prayers šŸ™ + +![demo image](assets/lidm.png) +> this is shown as in a terminal emulator, actual linux console doesn't support as much color and decorations + +## Features +* Builds fast af +* Works everywhere you can get gcc to compile +* Fast and possibly efficient +* Fully customizable, from strings to colors (I hope you know ansi escape codes), to action buttons +* Automatically detects xorg and wayland sessions, plus allowing to launch the default user shell (if enabled in config) + +## WIP +* Save last selection +* Config parsing, it's fully customizable, but everything hardcoded for now :) +* Long sessions, strings, usernames, passwords... they will just overflow or f*ck your terminal, I know it and I don't know if I'll fix it. + +## Forget it +* Any kind of arguments +* UTF characters, I'm using `strlen()` and treating characters as per byte basis, UTF-8 chars might work or not + +> [!CAUTION] +> (they should add `> [!DISCLAIMER]` fr) I wrote this readme with the same quality as the code, behing this keyboard there's half a brainrotcell left writing what it remembers of this program, so don't take this to seriously, I'm typing as I think without filter lol, but the program works, or should. Also, about any "TODO" in this readme (or the code), I didn't forget finishing it, I actually don't care + +# Backstory +I went into summer travel to visit family with an old laptop that barely supports x86_64, and ly recently added some avx2 instructions I think (I just get invalid op codes), manually building (any previous commit too) didn't work because of something in the `build.zig` file, so out of boredom I decided to craft up my own simple display manager on the only language this thing can handle, ✨C✨ (I hate this and reserve the right for the rust rewrite, actually solid). I spedrun it, basically did in in 3 days while touching *some* grass (:o), and I'm bad af in C, so this is spaghetti code on another level. I think it doesn't do almost nothing unsafe, I mean, I didn't check allocations and it's capable of reallocating memory until your username uses all memory and crashes the system due to a off by 1 error, but pretty consistent otherwise (probably). + +The name is just ly byt changing "y" with "i", that had a reason but forgot it, (maybe the i in *simple*), so I remembered this sh*tty laptop with a lid, this thing is also a display manager (dm, ly command is also `ly-dm`), so just did lidm due to all that. +![think gif](https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExcTFzaGVmb3VjN3FnOXV6OG9rMG91a2QwM3c0aDV0NWpoZjFzNDEwayZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/d3mlE7uhX8KFgEmY/giphy.gif) + +Btw, this laptop is so bad that I can't even render markdown in reasonable time, I'll just push this and fix render issues live :) + +# Index +(TODO, VSC(odium) does this automatically, I'm on nvim rn šŸ˜Ž) + +# Requirements +* A computer with unix based system. +* That system should have the resources neccessary for this program to make sense (sessions, users...) +* A compiler (optional, you can compile by hand, but I doubt you want to see the code) +* Make (also optional, but does things atomatically, make sure `gcc` and `mkdir -p` work as expected) + +# Compiling +```sh +make # šŸ‘ +``` + +# Configuring +Ugh, config will be a straigh copy of config defaults to `/etc` I think, there's no config yet :P and you need to enable the service, just do what ly does (I'm doing this on dinit, all init systems should be supported). + +# Contributing +Don't do this to yourself, but you can ofc, you can also fork or whatever (make sure to comply with GNU's GPLv3), but I want to do the rust rewrite 😔 + +# Recommendations +Hope you didn't expect actual project recommendations, but these songs are šŸ”„ +* "Sixpence None the Richer - Kiss Me" +* "Avril Lavigne - Complicated" +* "Shawn Mendes - There's Nothing Holdin' Me Back" +* "Rio Romeo - Nothing's New" +* "ElyOtto - SugarCrash!" +* "The Cranberries - Sunday" +* "Goo Goo Dolls - Iris" +* "Em Beihold - Numb Little Bug" +* "MAGIC! - Rude" +* "The Cranberries - Zombie" +* "Natalie Imbruglia - Torn" + +Oh, an actual recommendation, if you don't like a element you can change the fg color of it to be the same as the bg. + +Also (this isn't quite a recommendation lol), the default fg style shoulddisable decorators set up in other elements (cursivem underline... it's just adding 20 to the number btw, so if cursive is 4 (iirc), disabling it is 24). + +Congrats if you've managed to read through all this, wrote all this in exactly 30min. diff --git a/assets/lidm.png b/assets/lidm.png new file mode 100644 index 0000000000000000000000000000000000000000..23a4bac6e0aa3b7f9cae1afa42d8999265678dfe GIT binary patch literal 14056 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 +#include + +struct editable_field { + u_char length; + u_char pos; + char content[255]; +}; + +struct editable_field field_new(char*); +void field_trim(struct editable_field*, u_char); +void field_update(struct editable_field*, char*); +bool field_seek(struct editable_field*, char); + +#endif diff --git a/include/keys.h b/include/keys.h new file mode 100644 index 0000000..5933f32 --- /dev/null +++ b/include/keys.h @@ -0,0 +1,104 @@ +#ifndef _KEYSH_ +#define _KEYSH_ + +#include + +enum keys { + ESC, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + A_UP, + A_DOWN, + A_RIGHT, + A_LEFT, + N_CENTER, + N_UP, + N_DOWN, + N_RIGHT, + N_LEFT, + INS, + SUPR, + HOME, + END, + PAGE_UP, + PAGE_DOWN, +}; + +static const char * const key_names[] = { + [ESC] = "ESC", + [F1] = "F1", + [F2] = "F2", + [F3] = "F3", + [F4] = "F4", + [F5] = "F5", + [F6] = "F6", + [F7] = "F7", + [F8] = "F8", + [F9] = "F9", + [F10] = "F10", + [F11] = "F11", + [F12] = "F12", + [A_UP] = "A_UP", + [A_DOWN] = "A_DOWN", + [A_RIGHT] = "A_RIGHT", + [N_CENTER] = "N_CENTER", + [A_LEFT] = "A_LEFT", + [N_UP] = "N_UP", + [N_DOWN] = "N_DOWN", + [N_RIGHT] = "N_RIGHT", + [N_LEFT] = "N_LEFT", + [INS] = "INS", + [SUPR] = "SUPR", + [HOME] = "HOME", + [END] = "END", + [PAGE_UP] = "PAGE_UP", + [PAGE_DOWN] = "PAGE_DOWN", +}; + +struct key_mapping { + enum keys key; + const char *sequences[3]; +}; + +static const struct key_mapping key_mappings[] = { + { ESC, { "\x1b", NULL }}, + { F1, { "\x1bOP", "\x1b[[A", NULL }}, + { F2, { "\x1bOQ", "\x1b[[B", NULL }}, + { F3, { "\x1bOR", "\x1b[[C", NULL }}, + { F4, { "\x1bOS", "\x1b[[D", NULL }}, + { F5, { "\x1b[15~", "\x1b[[E", NULL }}, + { F6, { "\x1b[17~", NULL }}, + { F7, { "\x1b[18~", NULL }}, + { F8, { "\x1b[19~", NULL }}, + { F9, { "\x1b[20~", NULL }}, + { F10, { "\x1b[21~", NULL }}, + { F11, { "\x1b[23~", NULL }}, + { F12, { "\x1b[24~", NULL }}, + { A_UP, { "\x1b[A", NULL }}, + { A_DOWN, { "\x1b[B", NULL }}, + { A_RIGHT, { "\x1b[C", NULL }}, + { A_LEFT, { "\x1b[D", NULL }}, + { N_CENTER, { "\x1b[E", NULL }}, + { N_UP, { "\x1bOA", NULL }}, + { N_DOWN, { "\x1bOB", NULL }}, + { N_RIGHT, { "\x1bOC", NULL }}, + { N_LEFT, { "\x1bOD", NULL }}, + { INS, { "\x1b[2~", NULL }}, + { SUPR, { "\x1b[3~", NULL }}, + { HOME, { "\x1b[H", NULL }}, + { END, { "\x1b[F", NULL }}, + { PAGE_UP, { "\x1b[5~", NULL }}, + { PAGE_DOWN, { "\x1b[6~", NULL }}, +}; + +#endif diff --git a/include/sessions.h b/include/sessions.h index c4f1f6e..f6b689e 100644 --- a/include/sessions.h +++ b/include/sessions.h @@ -1,9 +1,18 @@ +#ifndef _SESSIONSH_ +#define _SESSIONSH_ + #include +enum session_type { + XORG, + WAYLAND, + SHELL, +}; + struct session { - const char *type; char *name; char *path; + enum session_type type; }; struct sessions_list { @@ -12,3 +21,5 @@ struct sessions_list { }; struct sessions_list *get_avaliable_sessions(); + +#endif diff --git a/include/ui.h b/include/ui.h new file mode 100644 index 0000000..9d6d576 --- /dev/null +++ b/include/ui.h @@ -0,0 +1,80 @@ +#ifndef _UIH_ +#define _UIH_ + +#include + +#include +#include +#include + +// should be ansi escape codes under \x1b[...m +// if not prepared accordingly, it might break +struct theme_colors { + char *bg; + char *fg; + char *err; + char *s_wl; + char *s_xorg; + char *s_shell; + char *f_other; + char *e_hostname; + char *e_date; + char *e_box; + char *e_header; + char *e_user; + char *e_passwd; + char *e_badpasswd; + char *e_key; +}; + +// even if they're multiple bytes long +// they should only take up 1 char size on display +struct theme_chars { + char *hb; + char *vb; + + char *ctl; + char *ctr; + char *cbl; + char *cbr; +}; + +struct theme { + struct theme_colors colors; + struct theme_chars chars; +}; + +struct functions { + enum keys poweroff; + enum keys reboot; + enum keys refresh; +}; + +struct strings { + char* f_poweroff; + char* f_reboot; + char* f_refresh; + char* e_user; + char* e_passwd; + char* s_xorg; + char* s_wayland; + char* s_shell; +}; + +struct behavior { + bool include_defshell; +}; + +struct config { + struct theme theme; + struct functions functions; + struct strings strings; + struct behavior behavior; +}; + +void setup(struct config); +int load(struct users_list*, struct sessions_list*); +void print_err(const char*); +void print_errno(const char*); + +#endif diff --git a/include/users.h b/include/users.h index 666a25e..d00354d 100644 --- a/include/users.h +++ b/include/users.h @@ -1,6 +1,10 @@ +#ifndef _USERSH_ +#define _USERSH_ + #include struct user { + char *shell; char *username; char *display_name; }; @@ -11,3 +15,5 @@ struct users_list { }; struct users_list *get_human_users(); + +#endif diff --git a/include/util.h b/include/util.h index 8e17069..fbfb026 100644 --- a/include/util.h +++ b/include/util.h @@ -1 +1,12 @@ +#ifndef _UTILH_ +#define _UTILH_ + +#include +#include +#include + +enum keys find_ansi(char*); +void read_press(u_char*, char*); void strcln(char **dest, const char *source); + +#endif diff --git a/src/efield.c b/src/efield.c new file mode 100644 index 0000000..fd9c891 --- /dev/null +++ b/src/efield.c @@ -0,0 +1,64 @@ +#include + +#include +#include + +struct editable_field field_new(char* content) { + struct editable_field __efield; + if(content != NULL) { + __efield.length = __efield.pos = strlen(content); + memcpy(__efield.content, content, __efield.length); + } else { + field_trim(&__efield, 0); + } + return __efield; +} + +void field_trim(struct editable_field *field, u_char pos) { + field->length = field->pos = pos; +} + +void field_update(struct editable_field *field, char *update) { + u_char insert_len = strlen(update); + if (insert_len == 0) + return; + + if (field->pos > field->length) + field->pos = field->length; // WTF + if (insert_len == 1) { + // backspace + if (*update == 127) { + if (field->pos < field->length) { + memcpy(&field->content[field->pos - 1], &field->content[field->pos], + field->length - field->pos); + } + (field->pos)--; + (field->length)--; + return; + } + } + + // append + if (field->length + field->pos >= 255) { + print_err("field too long"); + } + if (field->pos < field->length) { + // move with immediate buffer + memmove(&field->content[field->pos + insert_len], &field->content[field->pos], + field->length - field->pos); + } + memcpy(&field->content[field->pos], update, insert_len); + + field->pos += insert_len; + field->length += insert_len; +} + +// returns bool depending if it was able to "use" the seek +bool field_seek(struct editable_field *field, char seek) { + if(field->length == 0) return false; + + if(seek < 0 && -seek > field->pos) field->pos = 0; + else if(seek > 0 && 255 - field->pos < seek) field->pos = 255; + else field->pos += seek; + return true; +} diff --git a/src/main.c b/src/main.c index 721ce6c..05bc7bf 100644 --- a/src/main.c +++ b/src/main.c @@ -1,31 +1,77 @@ #include -#include +#include #include #include #include #include +#include #include -int main() { +static const struct config default_config() { + struct theme_colors __colors; + __colors.bg = "48;2;38;28;28"; + __colors.fg = "22;24;38;2;245;245;245"; + __colors.err = "1;31"; + __colors.s_wl = "38;2;255;174;66"; + __colors.s_xorg = "38;2;37;175;255"; + __colors.s_shell = "38;2;34;140;34"; + __colors.f_other = "38;2;255;64;64"; + __colors.e_hostname = "38;2;255;64;64"; + __colors.e_date = "38;2;144;144;144"; + __colors.e_box = "38;2;122;122;122"; + __colors.e_header = "4;38;2;0;255;0"; + __colors.e_user = "36"; + __colors.e_passwd = "4;38;2;245;245;205"; + __colors.e_badpasswd = "3;4;31"; + __colors.e_key = "38;2;255;174;66"; + + struct theme_chars __chars; + __chars.hb = "─"; + __chars.vb = "│"; + __chars.ctl = "ā”Œ"; + __chars.ctr = "┐"; + __chars.cbl = "ā””"; + __chars.cbr = "ā”˜"; + + struct theme __theme; + __theme.colors = __colors; + __theme.chars = __chars; + + + struct functions __functions; + __functions.poweroff = F1; + __functions.reboot = F2; + __functions.refresh = F5; + + struct strings __strings; + __strings.f_poweroff = "powewoff"; + __strings.f_reboot = "rewoot"; + __strings.f_refresh = "rewresh"; + __strings.e_user = "wuser"; + __strings.e_passwd = "passwd"; + __strings.s_xorg = "xworg"; + __strings.s_wayland = "waywand"; + __strings.s_shell = "swell"; + + struct behavior __behavior; + __behavior.include_defshell = true; + + struct config __config; + __config.theme = __theme; + __config.functions = __functions; + __config.strings = __strings; + __config.behavior = __behavior; + + return __config; +} + +int main(int argc, char* argv[]) { + setup(default_config()); + struct users_list *users = get_human_users(); struct sessions_list *sessions = get_avaliable_sessions(); - printf("users(%hu) sessions(%hu)\n", users->length, sessions->length); - - for (uint i = 0; i < users->length; i++) - printf("u[%d]: %s %s\n", i, users->users[i].username, - users->users[i].display_name); - - for (uint i = 0; i < sessions->length; i++) - printf("s[%d]: %s %s %s\n", i, sessions->sessions[i].type, - sessions->sessions[i].name, sessions->sessions[i].path); - - struct winsize w; - ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); - - printf("lines %d\n", w.ws_row); - printf("columns %d\n", w.ws_col); - - return 0; + int ret = load(users, sessions); + if(ret == 0) execl(argv[0], argv[0], NULL); } diff --git a/src/sessions.c b/src/sessions.c index 0aeb8c0..64080b6 100644 --- a/src/sessions.c +++ b/src/sessions.c @@ -9,13 +9,17 @@ #include #include -static const char *sources[][2] = { - { "xorg", "/usr/share/xsessions" }, - { "wl", "/usr/share/wayland-sessions" }, +struct source_dir { + enum session_type type; + char* dir; +}; +static const struct source_dir sources[] = { + { XORG, "/usr/share/xsessions" }, + { WAYLAND, "/usr/share/wayland-sessions" }, }; static const size_t sources_size = sizeof(sources) / sizeof(sources[0]); -static struct session __new_session(const char *type, char *name, const char *path) { +static struct session __new_session(enum session_type type, char *name, const char *path) { struct session __session; __session.type = type; strcln(&__session.name, name); @@ -33,7 +37,7 @@ static u_int16_t used_size = 0; static struct session *sessions = NULL; static struct sessions_list *__sessions_list = NULL; -static const char* session_type; +static enum session_type session_type; static int fn(const char *fpath, const struct stat *sb, int typeflag) { // practically impossible to reach this // but will prevent break @@ -82,7 +86,7 @@ static int fn(const char *fpath, const struct stat *sb, int typeflag) { } static struct sessions_list __list; -// This code is designed to be run purely sinlge threaded +// This code is designed to be run purely single threaded struct sessions_list *get_avaliable_sessions() { if (sessions != NULL) return __sessions_list; @@ -90,8 +94,8 @@ struct sessions_list *get_avaliable_sessions() { sessions = malloc(alloc_size * unit_size); for (uint i = 0; i < sources_size; i++) { - session_type = sources[i][0]; - ftw(sources[i][1], &fn, 1); + session_type = sources[i].type; + ftw(sources[i].dir, &fn, 1); } sessions = realloc(sessions, used_size * unit_size); diff --git a/src/ui.c b/src/ui.c new file mode 100644 index 0000000..14c7e3f --- /dev/null +++ b/src/ui.c @@ -0,0 +1,425 @@ +// i'm sorry + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static void print_box(); +static void print_footer(); +static void restore_all(); +static void signal_handler(int); + +const uint boxw = 50; +const uint boxh = 12; + +struct uint_point { + uint x; + uint y; +}; + +static void print_session(struct uint_point, struct session, bool); +static void print_user(struct uint_point, struct user, bool); +static void print_passwd(struct uint_point, uint, bool); + +// ansi resource: https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797 +static struct termios orig_term; +static struct termios term; +static struct winsize window; + +static struct theme theme; +static struct functions functions; +static struct strings strings; +static struct behavior behavior; +void setup(struct config __config) { + ioctl(STDOUT_FILENO, TIOCGWINSZ, &window); + + // 2 padding top and bottom for footer and vertical compensation + // 2 padding left & right to not overflow footer width + if (window.ws_row < boxh + 4 || window.ws_col < boxw + 4) { + fprintf(stderr, "\x1b[1;31mScreen too small\x1b[0m\n"); + exit(1); + } + + theme = __config.theme; + functions = __config.functions; + strings = __config.strings; + + tcgetattr(STDOUT_FILENO, &orig_term); + term = orig_term; // save term + // "stty" attrs + term.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDOUT_FILENO, TCSANOW, &term); + + // save cursor pos, save screen, hide cursor, set color and reset screen + // (applying color to all screen) + printf("\x1b[s\x1b[?47h\x1b[%s;%sm\x1b[2J", theme.colors.bg, + theme.colors.fg); + + print_footer(); + atexit(restore_all); + signal(SIGINT, signal_handler); +} + +static struct uint_point box_start() { + struct uint_point __start; + __start.x = (window.ws_col - boxw) / 2 + 1; + __start.y = (window.ws_row - boxh) / 2 + 1; + return __start; +} + +static char *fmt_time() { + time_t t = time(NULL); + struct tm tm = *localtime(&t); + + size_t bsize = + snprintf(NULL, 0, "%d-%02d-%02d %02d:%02d:%02d", tm.tm_year + 1900, + tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec) + + 1; + char *buf = malloc(bsize); + snprintf(buf, bsize, "%d-%02d-%02d %02d:%02d:%02d", tm.tm_year + 1900, + tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + return buf; +} + +// TODO: handle buffers longer than the buffer (cut str to the end, change +// cursor pos...) should just overlap for now + +// ugh, this represent a field which might have options +// opts is the ammount of other options possible (0 will behave as a passwd) +// aaaand (it's an abstract idea, letme think), also holds the status of a +// custom content, like custom launch command or user or smth +struct opt_field { + uint opts; + uint current_opt; // 0 is edit mode btw + struct editable_field efield; +}; +static struct opt_field ofield_new(uint opts) { + struct opt_field __field; + __field.opts = opts; + __field.current_opt = 1; + if (opts == 0) { + __field.current_opt = 0; + __field.efield = field_new(""); + } + return __field; +} +static void ofield_toedit(struct opt_field *ofield, char *init) { + ofield->efield = field_new(init); +} +// true in case it was able to "use" the seek (a empty only editable field +// wouldn't) +static bool ofield_seek(struct opt_field *ofield, char seek) { + if (ofield->current_opt == 0) { + if (field_seek(&ofield->efield, seek)) { + return true; + } + } + + if (ofield->opts == 0) + return false; + + ofield->current_opt = (ofield->current_opt + seek + ofield->opts + 1) % + (ofield->opts + 1); // not sure about this one + return true; +} +static u_char ofield_max_displ_pos(struct opt_field *ofield) { + // TODO: set max cursor pos too + // keep in mind that also have to keep in mind scrolling and ughhh, mentally blocked, but this is complex + if(ofield->current_opt == 0) + return ofield->efield.pos; + else + return 0; +} + +enum input { SESSION, USER, PASSWD }; +static u_char inputs_n = 3; +enum input focused_input = PASSWD; +struct opt_field of_session; +struct opt_field of_user; +struct opt_field of_passwd; + +void update_cursor_focus() { + struct uint_point bstart = box_start(); + u_char line = bstart.y; + u_char row = bstart.x + 15; + if (focused_input == SESSION) { + line += 5; + row += ofield_max_displ_pos(&of_session); + } else if (focused_input == USER) { + line += 7; + row += ofield_max_displ_pos(&of_user); + } else if (focused_input == PASSWD) { + line += 9; + row += ofield_max_displ_pos(&of_passwd); + } + printf("\x1b[%d;%dH", line, row); + fflush(stdout); +} + +// true = forward, false = backward +void change_field_focus(bool direction) { + if (direction) + focused_input = (focused_input + 1 + inputs_n) % inputs_n; + else + focused_input = (focused_input - 1 + inputs_n) % inputs_n; +} + +int load(struct users_list *users, struct sessions_list *sessions) { + /// SETUP + + // hostnames larger won't render properly + char *hostname = malloc(16); + if (gethostname(hostname, 16) != 0) { + free(hostname); + hostname = "unknown"; + } else { + hostname = realloc(hostname, strlen(hostname) + 1); + } + + of_session = ofield_new(sessions->length + behavior.include_defshell); + of_user = ofield_new(users->length); + of_passwd = ofield_new(0); + + /// PRINTING + const struct uint_point boxstart = box_start(); + + // printf box + print_box(); + + // put hostname + printf("\x1b[%d;%dH\x1b[%sm%s\x1b[%sm", boxstart.y + 2, + boxstart.x + 12 - (uint)strlen(hostname), theme.colors.e_hostname, + hostname, theme.colors.fg); + + // put date + char *fmtd_time = fmt_time(); + printf("\x1b[%d;%dH\x1b[%sm%s\x1b[%sm", boxstart.y + 2, + boxstart.x + boxw - 3 - (uint)strlen(fmtd_time), theme.colors.e_date, + fmtd_time, theme.colors.fg); + + print_session(boxstart, sessions->sessions[0], false); + print_user(boxstart, users->users[0], false); + print_passwd(boxstart, 5, false); + fflush(stdout); + update_cursor_focus(); + + /// INTERACTIVE + u_char len; + char seq[256]; + + while (true) { + read_press(&len, seq); + if (*seq == '\x1b') { + enum keys ansi_code = find_ansi(seq); + if (ansi_code != -1) { + if (ansi_code == functions.refresh) { + restore_all(); + return 0; + } else if (ansi_code == functions.reboot) { + system("reboot"); + exit(0); + } else if (ansi_code == functions.poweroff) { + system("poweroff"); + exit(0); + } else if (ansi_code == A_UP || ansi_code == A_DOWN) { + change_field_focus(ansi_code == A_UP); + } + } + } else {} + /*printf("norm(%d): %d\n", len, *seq);*/ + } +} + +static char *line_cleaner = NULL; +static void clean_line(struct uint_point origin, uint line) { + if (line_cleaner == NULL) { + line_cleaner = malloc((boxw - 2) * sizeof(char)); + memset(line_cleaner, 32, boxw - 2); + } + printf("\x1b[%d;%dH", origin.y + line, origin.x + 1); + fflush(stdout); + write(STDOUT_FILENO, line_cleaner, boxw - 2); +} + +// TODO: session_len > 32 +static void print_session(struct uint_point origin, struct session session, + bool multiple) { + clean_line(origin, 5); + const char *session_type; + if (session.type == XORG) { + session_type = strings.s_xorg; + } else if (session.type == WAYLAND) { + session_type = strings.s_wayland; + } else if (session.type == SHELL) { + session_type = strings.s_shell; + } + printf("\r\x1b[%luC\x1b[%sm%s\x1b[%sm", origin.x + 11 - strlen(session_type), + theme.colors.e_header, session_type, theme.colors.fg); + + char *session_color; + if (session.type == XORG) { + session_color = theme.colors.s_xorg; + } else if (session.type == WAYLAND) { + session_color = theme.colors.s_wl; + } else if (session.type == SHELL) { + session_color = theme.colors.s_shell; + } + + if (multiple) { + printf("\r\x1b[%dC< \x1b[%sm%s\x1b[%sm >", origin.x + 14, session_color, + session.name, theme.colors.fg); + } else { + printf("\r\x1b[%dC\x1b[%sm%s\x1b[%sm", origin.x + 14, session_color, + session.name, theme.colors.fg); + } +} + +// TODO: user_len > 32 +static void print_user(struct uint_point origin, struct user user, + bool multiple) { + clean_line(origin, 7); + printf("\r\x1b[%luC\x1b[%sm%s\x1b[%sm", + origin.x + 11 - strlen(strings.e_user), theme.colors.e_header, + strings.e_user, theme.colors.fg); + + char *user_color = theme.colors.e_user; + + if (multiple) { + printf("\r\x1b[%dC< \x1b[%sm%s\x1b[%sm >", origin.x + 14, user_color, + user.display_name, theme.colors.fg); + } else { + printf("\r\x1b[%dC\x1b[%sm%s\x1b[%sm", origin.x + 14, user_color, + user.display_name, theme.colors.fg); + } +} + +static char *passwd_prompt[32]; +// TODO: passwd_len > 32 +static void print_passwd(struct uint_point origin, uint length, bool err) { + clean_line(origin, 9); + printf("\r\x1b[%luC\x1b[%sm%s\x1b[%sm", + origin.x + 11 - strlen(strings.e_passwd), theme.colors.e_header, + strings.e_passwd, theme.colors.fg); + + char *pass_color; + if (err) + pass_color = theme.colors.e_badpasswd; + else + pass_color = theme.colors.e_passwd; + + memset(passwd_prompt, 32, 32); + memset(passwd_prompt, '*', length); + + printf("\r\x1b[%dC\x1b[%sm\x1b[4m", origin.x + 14, pass_color); + fflush(stdout); + write(STDOUT_FILENO, passwd_prompt, 32); + printf("\x1b[%sm", theme.colors.fg); +} + +// ik this code is... *quirky* +// w just accounts for filler +// if filler == NULL, it will just move cursor +static void print_row(uint w, uint n, char *edge1, char *edge2, char **filler) { + char *row; + size_t row_size; + + uint size; + if (filler == NULL) { + row_size = snprintf(NULL, 0, "%s\x1b[%dC%s\x1b[%dD\x1b[1B", edge1, w, edge2, + w + 2) + + 1; + row = malloc(row_size); + snprintf(row, row_size, "%s\x1b[%dC%s\x1b[%dD\x1b[1B", edge1, w, edge2, + w + 2); + } else { + size_t fillersize = strlen(*filler) * w; + size_t nbytes1 = snprintf(NULL, 0, "%s", edge1) + 1; + size_t nbytes2 = snprintf(NULL, 0, "%s\x1b[%dD\x1b[1B", edge2, w + 2) + 1; + row_size = nbytes1 + fillersize + nbytes2; + row = malloc(row_size); + snprintf(row, nbytes1, "%s", edge1); + for (uint i = 0; i < fillersize; i += strlen(*filler)) { + strcpy(&row[nbytes1 + i], *filler); + } + snprintf(&row[nbytes1 + fillersize], nbytes2, "%s\x1b[%dD\x1b[1B", edge2, + w + 2); + } + + for (uint i = 0; i < n; i++) { + write(STDOUT_FILENO, row, row_size); + } + free(row); +} + +static void print_box() { + // TODO: check min sizes + const struct uint_point bstart = box_start(); + + printf("\x1b[%d;%dH\x1b[%sm", bstart.y, bstart.x, theme.colors.e_box); + fflush(stdout); + print_row(boxw - 2, 1, theme.chars.ctl, theme.chars.ctr, &theme.chars.hb); + print_row(boxw - 2, boxh - 2, theme.chars.vb, theme.chars.vb, NULL); + print_row(boxw - 2, 1, theme.chars.cbl, theme.chars.cbr, &theme.chars.hb); + printf("\x1b[%sm", theme.colors.fg); + fflush(stdout); +} + +static void print_footer() { + size_t bsize = snprintf(NULL, 0, "%s %s %s %s %s %s", strings.f_poweroff, + key_names[functions.poweroff], strings.f_reboot, + key_names[functions.reboot], strings.f_refresh, + key_names[functions.refresh]); + + uint row = window.ws_row - 1; + uint col = window.ws_col - 2 - bsize; + printf("\x1b[%3$d;%4$dH%8$s \x1b[%1$sm%5$s\x1b[%2$sm %9$s " + "\x1b[%1$sm%6$s\x1b[%2$sm %10$s \x1b[%1$sm%7$s\x1b[%2$sm", + theme.colors.e_key, theme.colors.fg, row, col, + key_names[functions.poweroff], key_names[functions.reboot], + key_names[functions.refresh], strings.f_poweroff, strings.f_reboot, + strings.f_refresh); + fflush(stdout); +} + +void print_err(const char *msg) { + struct uint_point origin = box_start(); + fprintf(stderr, "\x1b[%d;%dH%s(%d): %s", origin.y - 1, origin.x, msg, errno, + strerror(errno)); +} + +void print_errno(const char *descr) { + struct uint_point origin = box_start(); + if (descr == NULL) + fprintf(stderr, "\x1b[%d;%dH\x1b[%smunknown error(%d): %s", origin.y - 1, + origin.x, theme.colors.err, errno, strerror(errno)); + else { + fprintf(stderr, "\x1b[%d;%dH\x1b[%sm%s(%d): %s", origin.y - 1, origin.x, + descr, theme.colors.err, errno, strerror(errno)); + } +} + +void restore_all() { + // restore cursor pos, restore screen and show cursor + printf("\x1b[u\x1b[?47l\x1b[?25h"); + fflush(stdout); + tcsetattr(STDOUT_FILENO, TCSANOW, &orig_term); +} + +void signal_handler(int code) { + restore_all(); + exit(code); +} diff --git a/src/users.c b/src/users.c index d073a2a..810fd00 100644 --- a/src/users.c +++ b/src/users.c @@ -10,6 +10,7 @@ static struct user __new_user(struct passwd *p) { struct user __user; + strcln(&__user.shell, p->pw_shell); strcln(&__user.username, p->pw_name); if (p->pw_gecos[0] == '\0') __user.display_name = __user.username; @@ -29,7 +30,7 @@ static struct user *users = NULL; static struct users_list *__users_list = NULL; struct users_list __list; -// This code is designed to be run purely sinlge threaded +// This code is designed to be run purely single threaded struct users_list *get_human_users() { if (users != NULL) return __users_list; diff --git a/src/util.c b/src/util.c index b9231b7..29132f2 100644 --- a/src/util.c +++ b/src/util.c @@ -1,9 +1,58 @@ +#include #include #include +#include +#include +#include +#include #include +static int selret_magic(); + void strcln(char **dest, const char *source) { *dest = malloc(strlen(source) + sizeof(char)); strcpy(*dest, source); } + +enum keys find_ansi(char *seq) { + for (size_t i = 0; i < sizeof(key_mappings) / sizeof(key_mappings[0]); i++) { + struct key_mapping mapping = key_mappings[i]; + for (size_t j = 0; mapping.sequences[j] != NULL; j++) { + if (strcmp(mapping.sequences[j], seq) == 0) { + return (enum keys)i; + } + } + } + return -1; +} + +// https://stackoverflow.com/a/48040042 +void read_press(u_char *length, char *out) { + *length = 0; + + while (true) { + if (read(STDIN_FILENO, &out[(*length)++], 1) != 1) { + print_errno("read error"); + sleep(3); + exit(1); + } + int selret = selret_magic(); + if (selret == -1) { + print_errno("selret error"); + } else if (selret != 1) { + out[*length] = '\0'; + return; + } + } +} + +static int selret_magic() { + fd_set set; + struct timeval timeout; + FD_ZERO(&set); + FD_SET(STDIN_FILENO, &set); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + return select(1, &set, NULL, NULL, &timeout); +}