From cf9079b9373a63f4e267d1642f09b57fa8c78f46 Mon Sep 17 00:00:00 2001 From: wrmr Date: Sun, 24 Aug 2025 19:22:17 -0400 Subject: add pfp (likely a bit broken), user cache --- main.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 112 insertions(+), 18 deletions(-) diff --git a/main.c b/main.c index 6c2f871..d19ed5a 100644 --- a/main.c +++ b/main.c @@ -46,23 +46,24 @@ /* colors */ -typedef enum { - CPAIR_TEXT, CPAIR_MENTION, - CPAIR_USER, CPAIR_TIME, CPAIR_BORDER, - CPAIR_TAGLINE_OK, CPAIR_TAGLINE_WARN, CPAIR_TAGLINE_ERR -} ColorPair; - #define CPAIR_INIT_X(cp, fg, bg) init_pair(cp, fg, bg); -#define CPAIR_LIST\ +#define CPAIR_ENUM_X(cp, fg, bg) cp, +#define CPAIR_LIST(X)\ X(CPAIR_TEXT , COLOR_WHITE , COLOR_BLACK)\ X(CPAIR_MENTION , COLOR_BLACK , COLOR_WHITE)\ X(CPAIR_USER , COLOR_YELLOW , COLOR_BLACK)\ X(CPAIR_TIME , COLOR_BLUE , COLOR_BLACK)\ + X(CPAIR_PFP , COLOR_BLUE , COLOR_BLACK)\ + X(CPAIR_PFP_SELF , COLOR_YELLOW , 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)\ X(CPAIR_TAGLINE_ERR , COLOR_RED , COLOR_BLACK) +typedef enum { + CPAIR_LIST(CPAIR_ENUM_X) +} ColorPair; + /* dynamic arrays */ #define DYNARR(T) struct { ptrdiff_t len, cap; T *data; } @@ -88,9 +89,25 @@ typedef enum { (da)->len += n;\ } while(0) +/* options */ + +typedef struct { + int pfp_vis; + struct { + int left, right, top, bottom; + } margin; +} Options; + /* globals */ regex_t re_mention; +Options opt = { + .pfp_vis = 1, + .margin = { + .left = 8, + .right = 0 + } +}; /* tagline & logging */ @@ -157,6 +174,49 @@ int timestamp_invalid(struct timespec *ts) { return 0; } +/* user cache */ + +typedef struct { + Str name; + Str pfp; +} User; + +typedef struct UserCache UserCache; +struct UserCache { + Str key; + UserCache *child[4]; + User value; +}; + +typedef uint64_t Hash; +Hash str_hash(Str s) { + Hash h = 14695981039346656037LU; + for (int i = 0; i < s.n; i++) + h = (h ^ (s.s[i] & 255)) * 1099511628211LU; + return h; +} + +User *user_get_or_new(UserCache **ht, Str key, Arena *a) { + Hash hash = str_hash(key); + for (Hash h = hash; *ht; h >>= 2) { + if (str_eql(key, (*ht)->key)) return &(*ht)->value; + ht = &(*ht)->child[h & 3]; + } + if (!a) return NULL; + *ht = new(a, UserCache); + (*ht)->key = key; + return &(*ht)->value; +} + +User *user_get(UserCache *ht, Str key) { + Hash hash = str_hash(key); + for (Hash h = hash; ht; h >>= 2) { + if (str_eql(key, ht->key)) return &ht->value; + ht = ht->child[h & 3]; + } + return NULL; +} + /* posts */ typedef struct Post { @@ -168,6 +228,7 @@ typedef struct Post { typedef struct { ArenaMark mark; Post *data; + UserCache *users; ptrdiff_t len, cap; } PostList; @@ -177,6 +238,24 @@ typedef struct { int err; } PostStats; +/* load users */ + +void user_load(PostList *pl, Str name, Str binkdir, Arena *a) { + User *u = user_get_or_new(&pl->users, name, a); + u->name = name; + FILE *f = fopen(cstr_fmt(a, "%S/pfp.txt", binkdir), "r/o"); + if (f) { + read_all(f, &u->pfp, a); + fclose(f); + } else { + u->pfp = S( + "bi __ \n" + "nk_(''<\n" + " (____)\n" + ); + } +} + /* sorting */ int ts_cmp(const struct timespec *a, const struct timespec *b) { @@ -221,12 +300,14 @@ void posts_gather_from(PostList *posts, Str username, const char *path, Arena *a DIR *d = opendir(path); if (!d) return; post_stats.user_count++; + Str user = str_dup(username, a); + user_load(posts, user, str_from_cstr(path), a); for (struct dirent *de; (de = readdir(d)); ) { if (*de->d_name == '.') continue; Post p = { 0 }; if (str_to_timespec(str_from_cstr(de->d_name), &p.timestamp)) continue; if (timestamp_invalid(&p.timestamp)) continue; - p.user = str_dup(username, a); + p.user = user; p.path = cstr_fmt(a, "%s/%s", path, de->d_name); DA_PUSH(posts, p); } @@ -321,7 +402,7 @@ typedef struct { } Gfx; int gfx_post_width(void) { - return getmaxx(stdscr) - GFX_MARGIN_X * 2; + return getmaxx(stdscr) - GFX_MARGIN_X * 2 - opt.margin.left - opt.margin.right; } int gfx_wrap_width(void) { @@ -362,7 +443,7 @@ int gfx_post_height(GfxPost *post) { return post->lines + 2 + GFX_TEXT_MARGIN_Y * 2; } -void gfx_draw_post(GfxPost *post, int y, int x, int width) { +void gfx_draw_post(GfxPost *post, PostList *pl, int y, int x, int width) { int height = gfx_post_height(post); if (!post->drawn) { @@ -370,7 +451,9 @@ void gfx_draw_post(GfxPost *post, int y, int x, int width) { post->drawn = 1; } - int left = x, top = y, right = x + width - 1, bottom = y + height - 1; + int left = x + opt.margin.left, top = y + opt.margin.top; + int right = left + width - 1, bottom = top + height - 1; + int self = str_eql(post->src->user, str_from_cstr(getlogin())); color_set(CPAIR_BORDER, 0); gfx_hline(top, left, right - left, ACS_HLINE); @@ -387,10 +470,23 @@ void gfx_draw_post(GfxPost *post, int y, int x, int width) { Str s = str_trim(str_from_cstr(ctime(&(time_t){post->src->timestamp.tv_sec}))); mvaddnstr(y, right - s.n, s.s, s.n); + if (opt.pfp_vis) { + color_set(self ? CPAIR_PFP_SELF : CPAIR_PFP, 0); + User *u = user_get(pl->users, post->src->user); + if (u) { + Str line = { 0 }; + Str pfp = u->pfp; + for (int i = 0; next_line(&pfp, &line); i++) { + mvaddnstr(top + i + GFX_TEXT_MARGIN_Y, left - line.n - GFX_TEXT_MARGIN_X, line.s, line.n); + } + } + } + color_set(CPAIR_USER, 0); - mvaddnstr(y, x + 2, post->src->user.s, post->src->user.n); + mvaddnstr(y, left + 2, post->src->user.s, post->src->user.n); color_set(post->has_mention ? CPAIR_MENTION : CPAIR_TEXT, 0); + Str txt = post->text; for (int i = 0; i < post->lines; i++) { Cut c = str_cut(txt, '\n'); @@ -399,11 +495,11 @@ void gfx_draw_post(GfxPost *post, int y, int x, int width) { } } -void gfx_draw(Gfx *gfx, int cur) { +void gfx_draw(Gfx *gfx, PostList *pl, int cur) { int width = gfx_post_width(); erase(); for (int i = cur, y = GFX_MARGIN_Y; i < gfx->len && y < getmaxy(stdscr) - GFX_MARGIN_Y; i++) { - gfx_draw_post(&gfx->posts[i], y, GFX_MARGIN_X, width); + gfx_draw_post(&gfx->posts[i], pl, y, GFX_MARGIN_X, width); y += gfx_post_height(&gfx->posts[i]) + GFX_POST_SPACING; } } @@ -509,9 +605,7 @@ void init_curses(void) { keypad(stdscr, TRUE); curs_set(0); -#define X CPAIR_INIT_X - CPAIR_LIST -#undef X + CPAIR_LIST(CPAIR_INIT_X) } void fini_curses(void) { @@ -551,7 +645,7 @@ int main(void) { if (cur < 0) cur = 0; arena_reset(&temp_arena); - gfx_draw(&gfx, cur); + gfx_draw(&gfx, &posts, cur); switch (tagline_status) { case TAGLINE_OK: color_set(CPAIR_TAGLINE_OK, 0); break; case TAGLINE_WARN: color_set(CPAIR_TAGLINE_WARN, 0); break; -- cgit v1.2.3