diff options
| author | wrmr | 2025-10-21 15:39:49 -0400 |
|---|---|---|
| committer | wrmr | 2025-10-21 15:39:49 -0400 |
| commit | 3faf956b5f38746768a2a90b77f0c791a90b605b (patch) | |
| tree | 4d877c5e3c4326c9f3dbde8c1843aec8f43c6286 /main.c | |
| parent | 8d3fc8ee149604f31b15b56fb77e106e4327b7ec (diff) | |
| parent | 0148fc9cf70e836348c48a75c8da5103ad11aa4a (diff) | |
merge config file development branch
Diffstat (limited to 'main.c')
| -rw-r--r-- | main.c | 213 |
1 files changed, 191 insertions, 22 deletions
@@ -5,7 +5,6 @@ /* TODO: * - * - read ~user/.pronouns and put next to name * - add keybind to view info about a user, like .plan, .project etc * - maybe remove ncurses dependency * - consider setting up a proper log file for log_warn / log_err stuff @@ -73,6 +72,7 @@ X(CPAIR_DEFAULT , COLOR_FG , COLOR_BG, A_NORMAL , "default")\ X(CPAIR_TEXT , COLOR_FG , COLOR_BG, A_NORMAL , "text")\ X(CPAIR_MENTION , COLOR_FG , COLOR_BG, A_REVERSE, "mention")\ + X(CPAIR_HELP , COLOR_FG , COLOR_BG, A_REVERSE, "help")\ X(CPAIR_USER , COLOR_YELLOW , COLOR_BG, A_NORMAL , "user")\ X(CPAIR_PRONOUNS , COLOR_CYAN , COLOR_BG, A_NORMAL , "pronouns")\ X(CPAIR_DATE , COLOR_BLUE , COLOR_BG, A_NORMAL , "date")\ @@ -154,6 +154,9 @@ typedef struct { typedef struct { struct { + const char *cbinkrc; + } cmdline; + struct { int help; int pfp, pronouns, date; const char *datefmt; @@ -680,12 +683,151 @@ void gfx_draw_post(GfxPost *post, int y, int x, int width, Arena *scratch) { } } +void gfx_draw_msg(void) { + switch (msg_status) { + case MSG_OK: use_color(CPAIR_MSG_OK); break; + case MSG_WARN: use_color(CPAIR_MSG_WARN); break; + case MSG_ERR: use_color(CPAIR_MSG_ERR); break; + } + mvaddstr(getmaxy(stdscr) - 1, getmaxx(stdscr) - strlen(msg), msg); +} + +void init_curses(void); +void fini_curses(void); + +#define USAGE "cbink [-c CONFIG]" + +const char *help_long = + + "USAGE\n" + "\n" + " " USAGE "\n" + "\n" + "OPTIONS\n" + "\n" + " -c --config config file to use instead of default, or NONE\n" + "\n" + + "KEY BINDINGS\n" + "\n" + " c create post\n" + " e edit post\n" + " r refresh post list\n" + " i view info of post author\n" + " p edit your profile picture\n" + " v view post in external pager\n" + " q quit cbink\n" + " ? open this help\n" + " Tab toggle profile pictures\n" + " g scroll to top\n" + " G scroll to bottom\n" + " j/Down post down\n" + " k/Up post up\n" + " PgDn/Space page down\n" + " PgUp/b page up\n" + " d/Ctrl-d half-page down\n" + " u/Ctrl-u half-page up\n" + "\n" + "CONFIGURATION\n" + "\n" + "~/.cbinkrc is an INI-style config file, with [sections], #comments, and key=value pairs.\n" + "\n" + "The sections are as follows:\n" + "\n" + " [cbink]\n" + "\n" + " help = 1 | 0 enable or disable short help banner\n" + " pfp = 1 | 0 enable or disable profile pictures\n" + " pronouns = 1 | 0 enable or disable user pronouns in posts\n" + " date = 1 | 0 enable or disable post timestamps\n" + " datefmt = ... configure timestamp format; see date(1) or strftime(1)\n" + "\n" + " [margin]\n" + "\n" + " left = N margin at left of screen\n" + " right = N margin at right of screen\n" + " top = N margin at top of screen\n" + " bottom = N margin at bottom of screen\n" + " text.x = N horizontal margin around post text\n" + " text.y = N vertical margin around post text\n" + " pfp = N margin between profile picture and post\n" + "\n" + " [color]\n" + "\n" + " All values in the color section have the same format ([ATTR1,...]FG[:BG]) --- a\n" + " foreground color, optionally followed by a colon and a background color, optionally\n" + " with comma-separated attributes.\n" + "\n" + " Foreground and background colors can be default, black, red, green, yellow, blue,\n" + " magenta, cyan, white, and hexadecimal RGB (#RRGGBB) if the terminal supports it.\n" + "\n" + " Attributes can be standout (highlighted), underline, blink, dim, bold, and italic.\n" + "\n" + " Keys in this section:\n" + "\n" + " default configure what 'default' means in foreground/background\n" + " text normal post text\n" + " mention text in a post that mentions you\n" + " help short help banner at bottom of screen\n" + " user post author username\n" + " pronouns post author pronouns\n" + " date post timestamp\n" + " pfp pfp of other users\n" + " pfp.self your own pfp\n" + " banner banner in user profile\n" + " border border around posts\n" + " msg.ok successful\n" + " msg.warn unsuccessful but correct\n" + " msg.err error occurred\n" + "\n" + "FILES\n" + "\n" + " ~/.cbinkrc\n" + " ~/.config/cbink/cbinkrc\n" + " configuration and styling\n" + "\n" + " ~/.binkbanner\n" + " user profile banner image\n" + "\n" + " ~/.binkpfp\n" + " user profile picture\n" + "\n" + " ~/.pronouns\n" + " user pronouns\n" + "\n" + " ~/.bink\n" + " directory of posts\n" + "\n" + ; + +const char *help_short = "[c]reate [e]dit [r]efresh [q]uit [?]help" + " | scrolling: arrows, j/k, b/space, page up/down, (ctrl-)d/u"; + +void gfx_draw_help_banner(Arena *scratch) { + use_color(CPAIR_HELP); + Str wrapped = { 0 }; + str_cat_wrap(&wrapped, str_from_cstr(help_short), getmaxx(stdscr), scratch); + int line_count = 0; + for (Str src = wrapped, line = { 0 }; next_line(&src, &line);) line_count++; + int y = getmaxy(stdscr) - line_count; + for (Str src = wrapped, line = { 0 }; next_line(&src, &line);) { + mvaddnstr(y++, 0, line.s, line.n); + for (int i = getmaxx(stdscr); i > line.n; i--) { + addch(' '); + } + } +} + void gfx_draw(Gfx *gfx, int cur, Arena *scratch) { erase(); for (int i = cur, y = opt.margin.top; i < gfx->len && y < getmaxy(stdscr) - opt.margin.bottom; i++) { gfx_draw_post(&gfx->posts[i], y, post_left_margin(gfx->posts[i].src), gfx_post_width(gfx->posts[i].src), scratch); y += gfx_post_height(&gfx->posts[i]) + GFX_POST_SPACING; } + if (opt.cbink.help) { + gfx_draw_help_banner(scratch); + } + gfx_draw_msg(); } /* maybe should write an ui layer to not have to deal with all the manual layout stuff */ @@ -803,8 +945,6 @@ char *get_editor(void) { return editor; } -void init_curses(void); -void fini_curses(void); int edit_file(const char *path, Arena *temp) { fini_curses(); int r = system(cstr_fmt(temp, "%s %s", get_editor(), path)); @@ -927,13 +1067,20 @@ void fini_curses(void) { /* configuration */ FILE *open_config_file(Arena *a) { - const char *s = cstr_fmt(a, "/home/%s/.cbinkrc", getlogin()); - FILE *f = fopen(s, "r/o"); - if (f) return f; - s = cstr_fmt(a, "/home/%s/.config/cbink/cbinkrc", getlogin()); + FILE *f; + const char *s = opt.cmdline.cbinkrc; + if (s) { + if (!strcmp(s, "NONE")) { + return NULL; + } + } else { + s = cstr_fmt(a, "/home/%s/.cbinkrc", getlogin()); + f = fopen(s, "r/o"); + if (f) return f; + s = cstr_fmt(a, "/home/%s/.config/cbink/cbinkrc", getlogin()); + } f = fopen(s, "r/o"); - if (f) return f; - return NULL; + return f; } typedef struct { @@ -976,7 +1123,6 @@ int sect_cbink(Str k, Str v, Arena *perm) { PROP_BOOL("pronouns", &opt.cbink.pronouns); PROP_BOOL("date", &opt.cbink.date); return -1; - return -1; } int sect_margin(Str k, Str v, Arena *perm) { @@ -1124,9 +1270,9 @@ int load_config(Arena *perm, Arena *temp) { continue; } if (!fn) continue; - line = str_trim(line); + line = str_trim(str_cut(line, '#').head); + line = str_trim(str_cut(line, ';').head); if (!line.n) continue; - if (line.s[0] == '#') continue; Cut c = str_cut(line, '='); c.head = str_trim(c.head); c.tail = str_trim(c.tail); @@ -1145,7 +1291,20 @@ int load_config(Arena *perm, Arena *temp) { /* main */ -int main(void) { +void page_string(Str txt) { + fini_curses(); + /* -c to prevent screen-not-fully-cleared weirdness */ + FILE *f = popen("less -c", "w"); + if (f) { + fwrite(txt.s, 1, txt.n, f); + pclose(f); + } else { + log_err("failed to open less"); + } + init_curses(); +} + +int main(int argc, const char **argv) { /* init */ Arena cfg_arena = { 0 }; @@ -1156,6 +1315,20 @@ int main(void) { arena_reserve(&post_arena, 128 << 10L); arena_reserve(&gfx_arena, 128 << 10L); + int optf = 0; + for (;;) { + int o = opt_next(&optf, &argc, &argv, ":c[config]"); + if (!o) break; + switch (o) { + case 'c': + opt.cmdline.cbinkrc = *argv; + break; + default: + fprintf(stderr, "Usage: " USAGE "\n"); + return 1; + } + } + ensure_dotbink_exists(&temp_arena); PostList posts = { 0 }; @@ -1178,12 +1351,6 @@ int main(void) { arena_reset(&temp_arena); gfx_draw(&gfx, cur, &temp_arena); - switch (msg_status) { - case MSG_OK: use_color(CPAIR_MSG_OK); break; - case MSG_WARN: use_color(CPAIR_MSG_WARN); break; - case MSG_ERR: use_color(CPAIR_MSG_ERR); break; - } - mvaddstr(getmaxy(stdscr) - 1, getmaxx(stdscr) - strlen(msg), msg); msg[0] = '\0'; int ch = getch(); @@ -1255,14 +1422,15 @@ resize: gfx_load(&gfx, &posts, &gfx_arena); break; case 'v': - fini_curses(); - system(cstr_fmt(&temp_arena, "less %s", posts.data[cur].path)); - init_curses(); + page_string(posts.data[cur].text); break; case 'i': gfx_draw_user(posts.data[cur].user, &temp_arena); (void)getch(); break; + case '?': + page_string(str_from_cstr(help_long)); + break; } } @@ -1275,6 +1443,7 @@ resize: arena_free(&temp_arena); fini_curses(); + puts(""); return 0; } |
