summaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c130
1 files 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;