summaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c216
1 files changed, 175 insertions, 41 deletions
diff --git a/main.c b/main.c
index b244eaf..0ec3a14 100644
--- a/main.c
+++ b/main.c
@@ -70,6 +70,7 @@
X(CPAIR_TIME , COLOR_BLUE , COLOR_BLACK)\
X(CPAIR_PFP , COLOR_BLUE , COLOR_BLACK)\
X(CPAIR_PFP_SELF , COLOR_YELLOW , COLOR_BLACK)\
+ X(CPAIR_BANNER , COLOR_WHITE , COLOR_BLACK)\
X(CPAIR_BORDER , COLOR_BLUE , COLOR_BLACK)\
X(CPAIR_TAGLINE_OK , COLOR_GREEN , COLOR_BLACK)\
X(CPAIR_TAGLINE_WARN , COLOR_YELLOW , COLOR_BLACK)\
@@ -223,23 +224,24 @@ int timestamp_invalid(struct timespec *ts) {
typedef struct {
int lines, cols;
Str *line;
-} Pfp;
+} Pic;
typedef struct {
Str name;
Str pronouns;
- Pfp pfp;
+ Pic pfp, banner;
+ unsigned post_count;
} User;
-void pfp_load(Pfp *pfp, Str src, Arena *a) {
+void pic_load(Pic *pfp, Str src, int min_lines, int min_cols, Arena *a) {
int lines = 0, cols = 0;
for (Str s = src, l = {0}; next_line(&s, &l);) {
int w = fast_utf8_width(l);
if (w > cols) cols = w;
lines++;
}
- pfp->lines = lines > GFX_PFP_MIN_LINES ? lines : GFX_PFP_MIN_LINES;
- pfp->cols = cols > GFX_PFP_MIN_COLS ? cols : GFX_PFP_MIN_COLS;
+ pfp->lines = MAX(lines, min_lines);
+ pfp->cols = MAX(cols, min_cols);
pfp->line = new_arr(a, Str, pfp->lines);
lines = 0;
for (Str s = src, l = {0}; next_line(&s, &l);) {
@@ -250,26 +252,32 @@ void pfp_load(Pfp *pfp, Str src, Arena *a) {
}
}
+void pic_load_path(Pic *dest, const char *path, Str or_src, int min_lines, int min_cols, Arena *a) {
+ FILE *f = fopen(path, "r/o");
+ if (f) {
+ Str pic_src = {0};
+ read_all(f, &pic_src, a);
+ fclose(f);
+ pic_load(dest, pic_src, min_lines, min_cols, a);
+ } else {
+ pic_load(dest, or_src, min_lines, min_cols, a);
+ }
+}
+
User *user_load(Str name, Str homedir, Str binkdir, Arena *a) {
(void)binkdir;
User *u = new(a, User);
u->name = str_dup(name, a);
- FILE *f = fopen(cstr_fmt(a, "%S/.binkpfp", homedir), "r/o");
- Str pfp_src = {0};
- if (f) {
- read_all(f, &pfp_src, a);
- fclose(f);
- } else {
- pfp_src = S(
+ pic_load_path(&u->pfp, cstr_fmt(a, "%S/.binkpfp", homedir), S(
"bi __ \n"
"nk_(''<\n"
" (____)\n"
- );
- }
- pfp_load(&u->pfp, pfp_src, a);
+ ), GFX_PFP_MIN_LINES, GFX_PFP_MIN_COLS, a);
- f = fopen(cstr_fmt(a, "%S/.pronouns", homedir), "r/o");
+ pic_load_path(&u->banner, cstr_fmt(a, "%S/.binkbanner", homedir), S(""), 0, 0, a);
+
+ FILE *f = fopen(cstr_fmt(a, "%S/.pronouns", homedir), "r/o");
if (f) {
Str buf = { 0 };
read_all(f, &buf, a);
@@ -357,6 +365,7 @@ void posts_gather_from(PostList *posts, Str username, Str homedir, Arena *a) {
p.user = user;
p.path = cstr_fmt(a, "%S/%s", binkdir, de->d_name);
DA_PUSH(posts, p);
+ user->post_count++;
}
closedir(d);
}
@@ -508,6 +517,54 @@ int gfx_post_height(GfxPost *post) {
return base;
}
+void gfx_draw_box(int y, int x, int height, int width) {
+ color_set(CPAIR_BORDER, 0);
+ int right = x + width - 1, bottom = y + height - 1;
+ gfx_hline(y, x, right - x, ACS_HLINE);
+ gfx_vline(y, x, height - 1, ACS_VLINE);
+ gfx_vline(y, right, height - 1, ACS_VLINE);
+ gfx_hline(bottom, x, right - x, ACS_HLINE);
+ mvaddch(bottom, right, ACS_LRCORNER);
+ mvaddch(bottom, x, ACS_LLCORNER);
+ mvaddch(y, x, ACS_ULCORNER);
+ mvaddch(y, right, ACS_URCORNER);
+}
+
+void gfx_cleared_box(int y, int x, int height, int width) {
+ for (int i = y; i < y + height; i++) {
+ for (int j = x; j < x + width; j++) {
+ mvaddch(i, j, ' ');
+ }
+ }
+ gfx_draw_box(y, x, height, width);
+}
+
+typedef enum {
+ PIC_NONE,
+ PIC_CTR_Y = 1,
+ PIC_CTR_X = 2,
+ PIC_LEFT = 4,
+ PIC_TOP = 8,
+ PIC_BORDER = 16,
+} PicDrawFlags;
+
+void gfx_draw_pic(Pic *pfp, int y, int x, int max_lines, int max_cols, PicDrawFlags f) {
+ int w = 0;
+ int h = MIN(pfp->lines, max_lines);
+ for (int i = 0; i < h; i++) {
+ w = MAX(w, MIN(pfp->line[i].n, max_cols));
+ }
+ if (f & PIC_CTR_X) x -= w >> 1;
+ if (f & PIC_CTR_Y) y -= h >> 1;
+ if (f & PIC_LEFT) x -= w;
+ if (f & PIC_TOP) y -= h;
+ for (int i = 0; i < h; i++) {
+ int n = utf8_cp_idx(pfp->line[i], max_cols);
+ mvaddnstr(y + i, x, pfp->line[i].s, n);
+ }
+ if (f & PIC_BORDER) gfx_draw_box(y - 1, x - 1, h + 2, w + 2);
+}
+
void gfx_draw_post(GfxPost *post, int y, int x, int width, Arena *scratch) {
int height = gfx_post_text_height(post);
int total_height = gfx_post_height(post);
@@ -518,26 +575,16 @@ void gfx_draw_post(GfxPost *post, int y, int x, int width, Arena *scratch) {
}
int left = x + post_left_margin(post->src), top = y + opt.margin.top;
- int right = left + width - 1, bottom = top + height - 1;
+ int right = left + width - 1;
int self = is_post_mine(post->src);
User *u = post->src->user;
if (total_height > height) {
int ydiff = (total_height - height) >> 1;
top += ydiff;
- bottom += ydiff;
}
- color_set(CPAIR_BORDER, 0);
- gfx_hline(top, left, right - left, ACS_HLINE);
- gfx_vline(top, left, height - 1, ACS_VLINE);
- gfx_vline(top, right, height - 1, ACS_VLINE);
- gfx_hline(bottom, left, right - left, ACS_HLINE);
-
- mvaddch(bottom, right, ACS_LRCORNER);
- mvaddch(bottom, left, ACS_LLCORNER);
- mvaddch(top, left, ACS_ULCORNER);
- mvaddch(top, right, ACS_URCORNER);
+ gfx_draw_box(top, left, height, width);
color_set(CPAIR_TIME, 0);
Str s = str_trim(str_from_cstr(ctime(&(time_t){post->src->timestamp.tv_sec})));
@@ -545,19 +592,16 @@ void gfx_draw_post(GfxPost *post, int y, int x, int width, Arena *scratch) {
if (opt.see.pfp) {
color_set(self ? CPAIR_PFP_SELF : CPAIR_PFP, 0);
- Pfp *pfp = &u->pfp;
- int h = MIN(pfp->lines, MAX(post->lines, GFX_PFP_MAX_LINES));
- int pfptop = y + GFX_TEXT_MARGIN_Y;
- if (height > h) pfptop += (height - h) >> 1;
- int pfpleft = left - pfp->cols - GFX_PFP_MARGIN;
- for (int i = 0; i < h; i++) {
- if (pfp->line[i].n > GFX_PFP_MAX_COLS) {
- int w = utf8_cp_idx(pfp->line[i], GFX_PFP_MAX_COLS);
- mvaddnstr(pfptop + i, pfpleft, pfp->line[i].s, w);
- } else {
- mvaddnstr(pfptop + i, pfpleft, pfp->line[i].s, pfp->line[i].n);
- }
- }
+ Pic *pfp = &u->pfp;
+ int pfpy = y;
+ int h = MIN(pfp->lines, GFX_PFP_MAX_LINES);
+ if (height > h) pfpy += (height - h) >> 1;
+ gfx_draw_pic(pfp,
+ pfpy,
+ left - GFX_PFP_MARGIN,
+ GFX_PFP_MAX_LINES,
+ GFX_PFP_MAX_COLS,
+ PIC_LEFT);
}
color_set(CPAIR_USER, 0);
@@ -586,6 +630,92 @@ void gfx_draw(Gfx *gfx, int cur, Arena *scratch) {
}
}
+/* maybe should write an ui layer to not have to deal with all the manual layout stuff */
+
+/*
+ * ui_begin(&gfx, .align = UI_CTR_X | UI_CTR_Y);
+ * ui_begin_row(&gfx);
+ * ui_begin_col(&gfx, .align = UI_CTR_X | UI_CTR_Y);
+ * ui_pic(&gfx, &user->banner);
+ * ui_end_col(&gfx);
+ * ui_end_row(&gfx);
+ * ui_begin_row(&gfx);
+ * ui_begin_col(&gfx, .align = UI_CTR_Y);
+ * ui_pic(&gfx, &user->pfp);
+ * ui_end_col(&gfx);
+ * ui_begin_col(&gfx, .border = 1);
+ * ui_text_row(&gfx, user->name);
+ * ui_text_row(&gfx, user->pronouns);
+ * ui_text_row(&gfx, str_fmt(scratch, "%u binks", user->post_count));
+ * ui_end_col(&gfx);
+ * ui_end_row(&gfx);
+ * ui_end(&gfx);
+ *
+ */
+
+/*
+ * ui_begin(&gfx, .align = UI_CTR_X | UI_CTR_Y);
+ * UI_ROW(&gfx, {
+ * ui_begin_col(&gfx, .align = UI_CTR_X | UI_CTR_Y);
+ * ui_pic(&gfx, &user->banner);
+ * ui_end_col(&gfx);
+ * });
+ * UI_ROW(&gfx, {
+ * UI_COL(&gfx, ui_pic(&gfx, &user->pfp));
+ * UI_COL(&gfx, {
+ * ui_text_row(&gfx, user->name);
+ * ui_text_row(&gfx, user->pronouns);
+ * ui_text_row(&gfx, str_fmt(scratch, "%u binks", user->post_count));
+ * }, .border = 1);
+ * });
+ * ui_end(&gfx);
+ *
+ */
+
+void gfx_draw_user(User *user, Arena *scratch) {
+ int cy = getmaxy(stdscr) >> 1, cx = getmaxx(stdscr) >> 1;
+
+ const char *binks = cstr_fmt(scratch, "%u binks", user->post_count);
+ int box_height = 3 + GFX_TEXT_MARGIN_Y;
+ int box_width = MAX((int)strlen(binks), MAX(fast_utf8_width(user->name), fast_utf8_width(user->pronouns)));
+
+ int total_height = (user->banner.lines ? user->banner.lines + 1 : 0) + MAX(box_height, user->pfp.lines) + GFX_TEXT_MARGIN_Y * 2;
+ int total_width = MAX(user->banner.cols, user->pfp.cols + 2 + box_width) + GFX_TEXT_MARGIN_X * 2;
+ gfx_cleared_box(cy - user->banner.lines - 2 - GFX_TEXT_MARGIN_Y, cx - (total_width >> 1) - 2 - GFX_TEXT_MARGIN_X,
+ total_height + 4, total_width + 4);
+
+ if (user->banner.lines > 0) {
+ color_set(CPAIR_BANNER, 0);
+ gfx_draw_pic(&user->banner, cy, cx, cy, cx, PIC_CTR_X | PIC_TOP);
+ } else {
+ //cy -= MAX(user->pfp.lines, box_height) >> 1;
+ cy--;
+ }
+
+ int left = cx - (total_width >> 1);
+ color_set(CPAIR_PFP, 0);
+ int pfpy = cy + 1;
+ if (box_height > user->pfp.lines) {
+ pfpy += (box_height - user->pfp.lines) >> 1;
+ }
+ gfx_draw_pic(&user->pfp, pfpy, left, GFX_PFP_MAX_LINES, GFX_PFP_MAX_COLS, 0);
+ color_set(CPAIR_USER, 0);
+ left += user->pfp.cols + 2;
+
+ if (user->pfp.lines > box_height) {
+ cy += (user->pfp.lines - box_height) >> 1;
+ }
+
+ int y = cy + 1;
+ mvaddnstr(y++, left + GFX_TEXT_MARGIN_X, user->name.s, user->name.n);
+ if (user->pronouns.n > 0) {
+ color_set(CPAIR_PRONOUNS, 0);
+ mvaddnstr(y++, left + GFX_TEXT_MARGIN_X, user->pronouns.s, user->pronouns.n);
+ }
+ color_set(CPAIR_TEXT, 0);
+ mvaddstr(y++, left + GFX_TEXT_MARGIN_X, binks);
+}
+
/* will skip at least one post, so not suitable for just scrolling one line at
* a time like bink.py does */
int gfx_line_skip(Gfx *gfx, int cur, int amt) {
@@ -814,6 +944,10 @@ resize:
system(cstr_fmt(&temp_arena, "less %s", posts.data[cur].path));
init_curses();
break;
+ case 'i':
+ gfx_draw_user(posts.data[cur].user, &temp_arena);
+ (void)getch();
+ break;
}
}