summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.c213
1 files changed, 191 insertions, 22 deletions
diff --git a/main.c b/main.c
index a94c446..b31a0af 100644
--- a/main.c
+++ b/main.c
@@ -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;
}