From b61f6f4d53803ecf83d9d6152a48bc39c1f2dd42 Mon Sep 17 00:00:00 2001 From: "Anna (navi) Figueiredo Gomes" Date: Thu, 9 Nov 2023 16:58:59 +0100 Subject: init Signed-off-by: Anna (navi) Figueiredo Gomes --- main.c | 391 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 391 insertions(+) create mode 100644 main.c (limited to 'main.c') diff --git a/main.c b/main.c new file mode 100644 index 0000000..1f33800 --- /dev/null +++ b/main.c @@ -0,0 +1,391 @@ +#include +#include +#include +#include +#include +#include + +#ifndef DATADIR +#define DATADIR "/usr/local" +#endif + +#define STARBOX_HEIGHT 12 +#define STARBOX_WIDTH 24 +#define INFOBOX_WIDTH 40 +#define STARBOX_MAX_TITLE ((STARBOX_WIDTH - 2) / 2) + +char *pick_constellation(void) { + char *xdg_dir = getenv("XDG_DATA_HOME"); + if (!xdg_dir) + xdg_dir = "~/.local/share"; + size_t len = snprintf(NULL, 0, "%s/%s", xdg_dir, "stargaze"); + char *user_dir = malloc(++len); + snprintf(user_dir, len, "%s/%s", xdg_dir, "stargaze"); + + const char * const dirs[] = { + "./stargaze/", + user_dir, + DATADIR "/stargaze/", + }; + + char **files = calloc(10, sizeof(*files)); + size_t nfiles = 0, max_files = 10; + for (size_t i = 0; i < sizeof(dirs) / sizeof(*dirs); i++) { + DIR *dir = opendir(dirs[i]); + if (!dir) + continue; + + struct dirent *dirent; + while ((dirent = readdir(dir))) { + if (dirent->d_type != DT_REG) + continue; + if (nfiles + 1 > max_files) + files = (max_files += 10, realloc(files, sizeof(*files) * max_files)); + size_t len = snprintf(NULL, 0, "%s/%s", dirs[i], dirent->d_name); + files[nfiles] = malloc(++len); + snprintf(files[nfiles++], len, "%s/%s", dirs[i], dirent->d_name); + } + closedir(dir); + } + + char *out = NULL; + if (nfiles > 0) { + srand(time(NULL)); + int choice = rand() % nfiles; + out = files[choice]; + files[choice] = files[--nfiles]; + } + + for (size_t i = 0; i < nfiles; i++) + free(files[i]); + free(files); + + free(user_dir); + + return out; +} + +char *name_to_fullwidth(const char *name, size_t len) { + if (!name) return NULL; + char *new_name = calloc((len * 3) + 1, sizeof(char)); + + size_t j = 0; + for (size_t i = 0; i < len; i++) { + if (isalnum(name[i])) { + uint32_t codepoint = name[i] + 0xFEE0; + new_name[j++] = 0xE0 | (codepoint >> 12); + new_name[j++] = 0x80 | ((codepoint >> 6) & 0x3F); + new_name[j++] = 0x80 | (codepoint & 0x3F); + } else { + new_name[j++] = name[i]; + } + } + new_name[j] = '\0'; + + return new_name; +} + +enum attibute_types { + STAR_ATTR_QUADRANT, + STAR_ATTR_ASCENSION, + STAR_ATTR_DECLINATION, + STAR_ATTR_AREA, + STAR_ATTR_STARS, + STAR_ATTR_TOTAL +}; + +struct constellation { + char *name[2]; + size_t name_len[2]; + char *display_name[2]; + + struct attribute { + char *key; + char *shortname; + char *symbol; + char *value; + } attributes[STAR_ATTR_TOTAL]; + + size_t longest_attr; + + size_t n_stars, max_stars; + struct point { size_t x, y; } stars[]; +}; + +struct constellation *parse_constellation(const char *path) { + FILE *fp = NULL; + if (!(fp = fopen(path, "r"))) { + return NULL; + } + struct constellation *constellation = calloc(1, sizeof(*constellation) + sizeof(*constellation->stars) * 10); + constellation->max_stars = 10; + + char *line = NULL; + size_t line_bufsiz; + size_t len; + while ((len = getline(&line, &line_bufsiz, fp)) != -1) { + line[len - 1] = '\0'; + char *data = strchr(line, ' '); + size_t datalen = len - (data - line); + if (!data) continue; + *data++ = '\0'; + if (!*data) continue; + + if (strcmp(line, "star") == 0) { + size_t x, y; + if (sscanf(data, "%ld %ld", &x, &y) != 2) + continue; + if (x >= 20 || y >= 20) + continue; + if (constellation->n_stars + 1 < constellation->max_stars) { + constellation->max_stars += 5; + constellation = realloc(constellation, sizeof(*constellation) + sizeof(*constellation->stars) * constellation->max_stars); + } + constellation->stars[constellation->n_stars++] = (struct point) { .x = x, .y = y }; + + continue; + } else if (strcmp(line, "name") == 0) { + if (constellation->name[0]) + continue; + constellation->name[0] = strdup(data); + constellation->name_len[0] = datalen; + char *spc; + + if (constellation->name_len[0] >= STARBOX_MAX_TITLE && (spc = strchr(constellation->name[0], ' '))) { + *spc = '\0'; + + constellation->name[1] = spc + 1; + constellation->name_len[1] = constellation->name_len[0] - (constellation->name[1] - constellation->name[0]); + constellation->name_len[0] -= constellation->name_len[1]; + } + + constellation->display_name[0] = name_to_fullwidth(constellation->name[0], + constellation->name_len[0] > STARBOX_MAX_TITLE ? STARBOX_MAX_TITLE : constellation->name_len[0]); + constellation->display_name[1] = name_to_fullwidth(constellation->name[1], + constellation->name_len[1] > STARBOX_MAX_TITLE ? STARBOX_MAX_TITLE : constellation->name_len[1]); + + } else if (strcmp(line, "quadrant") == 0) { + if (constellation->attributes[STAR_ATTR_QUADRANT].value) + continue; + constellation->attributes[STAR_ATTR_QUADRANT] = (struct attribute) { + .shortname = "Quad", + .key = "Quadrant", + .symbol = "Q", + .value = strdup(data), + }; + } else if (strcmp(line, "ascension") == 0) { + if (constellation->attributes[STAR_ATTR_ASCENSION].value) + continue; + constellation->attributes[STAR_ATTR_ASCENSION] = (struct attribute) { + .shortname = "Asc", + .key = "Ascension", + .symbol = "α", + .value = strdup(data), + }; + } else if (strcmp(line, "declination") == 0) { + if (constellation->attributes[STAR_ATTR_DECLINATION].value) + continue; + constellation->attributes[STAR_ATTR_DECLINATION] = (struct attribute) { + .shortname = "Decl", + .key = "Declination", + .symbol = "δ", + .value = strdup(data), + }; + } else if (strcmp(line, "area") == 0) { + if (constellation->attributes[STAR_ATTR_AREA].value) + continue; + constellation->attributes[STAR_ATTR_AREA] = (struct attribute) { + .shortname = "Area", + .key = "Area", + .symbol = "A", + .value = strdup(data), + }; + } else if (strcmp(line, "n_stars") == 0) { + if (constellation->attributes[STAR_ATTR_STARS].value) + continue; + constellation->attributes[STAR_ATTR_STARS] = (struct attribute) { + .shortname = "Stars", + .key = "Main Stars", + .symbol = "✦", + .value = strdup(data), + }; + } + } + free(line); + + return constellation; +} + +void constellation_free(struct constellation* constellation) { + if (!constellation) + return; + free(constellation->name[0]); + free(constellation->display_name[0]); + free(constellation->display_name[1]); + for (size_t i = 0; i < STAR_ATTR_TOTAL; i++) { + free(constellation->attributes[i].value); + } + free(constellation); +} + +void usage(const char* name) { + fprintf(stderr, +"usage: %s [-s|-S] [-f ] [-c ] [-p ]\n\ +\n\ +-f loads a specific starfile\n\ +-c hex color for the stars and highligts\n\ +-s | -S symbol or shortname form for the info box\n\ +-p the padding between the star window and the info text\n\ +\n\ +starfiles are picked from ./stargaze, %s/share/stargaze and ${XDG_DATA_HOME}/stargaze (defaults to ~/.local/share)\n", + name, DATADIR); +} + +enum info_format { + INFO_FULL, + INFO_SHORT, + INFO_SYM +}; + +int main(int argc, char **argv) { + char *starfile = NULL; + char opt; + enum info_format format = INFO_FULL; + struct color { int r, g, b; bool set; } color; + size_t padding = 5; + while ((opt = getopt(argc, argv, "hf:Ssc:")) != -1) { + switch (opt) { case 'h': + usage(argv[0]); + return EXIT_SUCCESS; + case 'S': + format = INFO_SHORT; + break; + case 's': + format = INFO_SYM; + break; + case 'f': + starfile = optarg; + break; + case 'c': + if (!(color.set = sscanf(optarg, "%2x%2x%2x", &color.r, &color.g, &color.b) == 3)) + fprintf(stderr, "%s is not a valid rgb hex color.\n", optarg); + break; + case 'p': + padding = atoi(optarg); + break; + default: + usage(argv[0]); + return EXIT_FAILURE; + } + } + if (!starfile) + starfile = pick_constellation(); + struct constellation *constellation = parse_constellation(starfile); + if (!constellation) { + fputs("failed to load constellation file\n", stderr); + return EXIT_FAILURE; + } + + struct notcurses_options opts = { + .flags = NCOPTION_CLI_MODE | NCOPTION_SUPPRESS_BANNERS | NCOPTION_DRAIN_INPUT, + }; + struct notcurses *nc = notcurses_core_init(&opts, NULL); + if (!nc) { + fputs("failed to init notcurses\n", stderr); + constellation_free(constellation); + return EXIT_FAILURE; + } + + struct ncplane *std = notcurses_stdplane(nc); + + unsigned cursor_y = ncplane_cursor_y(std); + unsigned term_width = ncplane_dim_x(std); + + if (term_width <= STARBOX_WIDTH) { + notcurses_stop(nc); + constellation_free(constellation); + fputs("terminal window\nis too small\n", stderr); + return EXIT_FAILURE; + } + + struct ncplane_options stargaze_opts = { + .x = 0, + .y = cursor_y, + .rows = STARBOX_HEIGHT, + .cols = term_width, + .flags = NCPLANE_OPTION_AUTOGROW | NCPLANE_OPTION_VSCROLL, + }; + struct ncplane *stargaze = ncplane_create(std, &stargaze_opts); + + struct ncplane_options star_opts = { + .x = 0, + .y = 0, + .rows = STARBOX_HEIGHT, + .cols = STARBOX_WIDTH, + }; + struct ncplane *stars = ncplane_create(stargaze, &star_opts); + + struct ncplane_options info_box_opts = { + .x = STARBOX_WIDTH, + .y = NCALIGN_CENTER, + .rows = STAR_ATTR_TOTAL + 2, // title and empty line + .cols = ncplane_dim_x(stargaze) - STARBOX_WIDTH, + .flags = NCPLANE_OPTION_VERALIGNED + }; + struct ncplane *info_box = ncplane_create(stargaze, &info_box_opts); + + ncplane_scrollup_child(std, stargaze); + + struct nccell box[6] = { 0 }; + nccells_light_box(stars, 0, 0, &box[0], &box[1], &box[2], &box[3], &box[4], &box[5]); + + ncplane_box_sized(stars, &box[0], &box[1], &box[2], &box[3], &box[4], &box[5], STARBOX_HEIGHT, STARBOX_WIDTH, + NCBOXGRAD_TOP | NCBOXGRAD_LEFT | NCBOXGRAD_RIGHT | NCBOXGRAD_BOTTOM); + + if (color.set) + ncplane_set_fg_rgb8(info_box, color.r, color.g, color.b); + ncplane_on_styles(info_box, NCSTYLE_BOLD); + if (constellation->name[0]) { + ncplane_putstr_aligned(stars, 0, NCALIGN_CENTER, constellation->display_name[0]); + ncplane_putstr_yx(info_box, 0, padding, constellation->name[0]); + } + if (constellation->name[1]) { + ncplane_putstr_aligned(stars, STARBOX_HEIGHT - 1, NCALIGN_CENTER, constellation->display_name[1]); + ncplane_printf(info_box, " %s", constellation->name[1]); + } + + for (size_t i = 0; i < constellation->n_stars; i++) + ncplane_putstr_yx(stars, constellation->stars[i].y, constellation->stars[i].x, "✦"); + + size_t info_index = 2; + for (size_t i = 0; i < STAR_ATTR_TOTAL; i++) { + if (!constellation->attributes[i].value) + continue; + char *key = NULL; + switch (format) { + case INFO_FULL: + key = constellation->attributes[i].key; + break; + case INFO_SHORT: + key = constellation->attributes[i].shortname; + break; + case INFO_SYM: + key = constellation->attributes[i].symbol; + break; + } + if (color.set) + ncplane_set_fg_rgb8(info_box, color.r, color.g, color.b); + ncplane_on_styles(info_box, NCSTYLE_BOLD); + ncplane_printf_yx(info_box, info_index++, padding, "%s: ", key); + + ncplane_set_fg_default(info_box); + ncplane_off_styles(info_box, NCSTYLE_BOLD); + ncplane_putstr(info_box, constellation->attributes[i].value); + } + + notcurses_render(nc); + + constellation_free(constellation); + notcurses_stop(nc); + puts("\n"); +} -- cgit v1.2.3