summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rwxr-xr-xenv/wsl/flake.lock30
-rw-r--r--flake.lock6
-rw-r--r--src/buffer/audio.c2
-rw-r--r--src/codec/libav/demuxer.c2
-rw-r--r--src/fruits/cmc/cmc.c562
-rw-r--r--src/libclient/client.c31
-rw-r--r--src/libclient/client.h2
-rw-r--r--src/libclient/resource_client.c9
-rw-r--r--src/libclient/resource_client.h5
-rw-r--r--src/libsink/sink.c6
-rw-r--r--src/tree/tree.c20
12 files changed, 443 insertions, 234 deletions
diff --git a/.gitignore b/.gitignore
index 8f48aae..aea0bfc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,11 @@
**/__pycache__/
-bin/
build*/
compile_commands.json
.cache/
.clangd/
data/
doc/**.html
+env/bin/
old/
old_old/
subprojects/c89atomic/
diff --git a/env/wsl/flake.lock b/env/wsl/flake.lock
index 749d367..f1c225f 100755
--- a/env/wsl/flake.lock
+++ b/env/wsl/flake.lock
@@ -21,11 +21,11 @@
"systems": "systems"
},
"locked": {
- "lastModified": 1701680307,
- "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
+ "lastModified": 1705309234,
+ "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
"owner": "numtide",
"repo": "flake-utils",
- "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
+ "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
"type": "github"
},
"original": {
@@ -41,11 +41,11 @@
]
},
"locked": {
- "lastModified": 1705875991,
- "narHash": "sha256-ssoo8WYXy4H0qQ81GhzOaMnvDmeXV02D4DdGOYRKCnU=",
+ "lastModified": 1706798041,
+ "narHash": "sha256-BbvuF4CsVRBGRP8P+R+JUilojk0M60D7hzqE0bEvJBQ=",
"owner": "nix-community",
"repo": "home-manager",
- "rev": "0021558dba313b6f494cd16362dcd4071f407535",
+ "rev": "4d53427bce7bf3d17e699252fd84dc7468afc46e",
"type": "github"
},
"original": {
@@ -62,11 +62,11 @@
"nixpkgs": "nixpkgs"
},
"locked": {
- "lastModified": 1705359964,
- "narHash": "sha256-ys1MDjIH6z5UP7gAciRfUAlf2FJV0t3yFib965N/S+I=",
+ "lastModified": 1706867893,
+ "narHash": "sha256-c5bADvtL35S3vsJaXR5YWTXe08W0gSwOrTOXfpJB4Ac=",
"owner": "nix-community",
"repo": "NixOS-WSL",
- "rev": "bb3eeeb96ce059ae29309138874ccf58e796f4b1",
+ "rev": "bcae8dc73b931b7f0fc65f1f1ef93dc379dfd66b",
"type": "github"
},
"original": {
@@ -78,11 +78,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1705183652,
- "narHash": "sha256-rnfkyUH0x72oHfiSDhuCHDHg3gFgF+lF8zkkg5Zihsw=",
+ "lastModified": 1705774713,
+ "narHash": "sha256-j6ADaDH9XiumUzkTPlFyCBcoWYhO83lfgiSqEJF2zcs=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "428544ae95eec077c7f823b422afae5f174dee4b",
+ "rev": "1b64fc1287991a9cce717a01c1973ef86cb1af0b",
"type": "github"
},
"original": {
@@ -94,11 +94,11 @@
},
"nixpkgs_2": {
"locked": {
- "lastModified": 1705677747,
- "narHash": "sha256-eyM3okYtMgYDgmYukoUzrmuoY4xl4FUujnsv/P6I/zI=",
+ "lastModified": 1706732774,
+ "narHash": "sha256-hqJlyJk4MRpcItGYMF+3uHe8HvxNETWvlGtLuVpqLU0=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "bbe7d8f876fbbe7c959c90ba2ae2852220573261",
+ "rev": "b8b232ae7b8b144397fdb12d20f592e5e7c1a64d",
"type": "github"
},
"original": {
diff --git a/flake.lock b/flake.lock
index c7d657f..06c1ded 100644
--- a/flake.lock
+++ b/flake.lock
@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1706191920,
- "narHash": "sha256-eLihrZAPZX0R6RyM5fYAWeKVNuQPYjAkCUBr+JNvtdE=",
+ "lastModified": 1706732774,
+ "narHash": "sha256-hqJlyJk4MRpcItGYMF+3uHe8HvxNETWvlGtLuVpqLU0=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "ae5c332cbb5827f6b1f02572496b141021de335f",
+ "rev": "b8b232ae7b8b144397fdb12d20f592e5e7c1a64d",
"type": "github"
},
"original": {
diff --git a/src/buffer/audio.c b/src/buffer/audio.c
index ce7a697..037d977 100644
--- a/src/buffer/audio.c
+++ b/src/buffer/audio.c
@@ -170,7 +170,7 @@ static bool push_internal(struct camu_audio_buffer *buf, u8 **data, s32 sample_c
static void push_av_frame_internal(struct camu_audio_buffer *buf, AVFrame *frame)
{
// It doesn't matter if there's a race here with flush(), as long
- // as any frame is from the same stream.
+ // as any pushed frame is from the same stream.
// A reset() must finish before any data is pushed.
if (al_atomic_u8_load(&buf->flow, AL_ATOMIC_RELAXED) == FLOWING) {
AVStream *stream = buf->stream->av.stream;
diff --git a/src/codec/libav/demuxer.c b/src/codec/libav/demuxer.c
index e276865..2169816 100644
--- a/src/codec/libav/demuxer.c
+++ b/src/codec/libav/demuxer.c
@@ -75,7 +75,7 @@ static bool lav_demuxer_init(struct camu_demuxer *demux, struct cch_handle *hand
al_log_info("lav_demux", "Guessing that stream #%u is an image.", i);
stream->duration = 0;
} else if (stream->duration < 0) {
- al_log_warn("lav_demux", "Stream #%u has an invalid duration (%ld).", i, av->format_context->duration);
+ al_log_warn("lav_demux", "Stream #%u has an invalid duration (%ld).", i, stream->duration);
if (av->format_context->duration < 0) {
stream->duration = 0;
} else {
diff --git a/src/fruits/cmc/cmc.c b/src/fruits/cmc/cmc.c
index 55f659c..f70cf8d 100644
--- a/src/fruits/cmc/cmc.c
+++ b/src/fruits/cmc/cmc.c
@@ -36,12 +36,14 @@ struct cmc_view {
struct ncplane *container;
u32 rows;
u32 y;
+ u32 pending_requests;
};
struct cmc_view_resource {
u16 id;
str unique_id;
u32 index;
+ array(struct cmc_view *) refs;
struct ncvisual *v;
struct ncvgeom geom;
struct ncvisual_options vopts;
@@ -79,17 +81,24 @@ struct cmc_view_page {
array(struct cmc_view *) views;
};
+enum {
+ SEARCH_NONE = 0,
+ SEARCH_UP,
+ SEARCH_DOWN
+};
+
struct cmc_search {
struct camu_search *search;
- bool find_anchor;
+ u8 find_anchor;
s32 anchor;
- s32 prev_anchor;
+ bool redraw;
u32 count;
s32 selected;
- s32 prev_selected;
+ array(struct cmc_view *) prev;
u32 max_page_num;
u32 min_page_num;
s32 end_of_page;
+ u32 pending_requests;
array(struct cmc_view_page *) pages;
};
@@ -98,6 +107,13 @@ struct cmc_tui {
u32 cols;
u32 rows;
struct ncplane *p;
+ struct {
+ struct ncplane *p;
+ u32 cols;
+ u32 rows;
+ bool active;
+ s32 selected;
+ } overlay;
struct aki_poll input;
u32 req_index;
array(struct cmc_search *) searches;
@@ -199,43 +215,6 @@ static u32 count_wrapped_lines(wstr *w, u32 width)
return wraps;
}
-static void date_to_relative_date(struct sho_post *post, char *out)
-{
- struct sho_post_date *created = NULL;
- struct sho_post_date *date;
- al_array_foreach_ptr(post->dates, i, date) {
- if (date->type == SHOKI_DATE_CREATED) {
- created = date;
- break;
- }
- }
- if (!created) return;
- f64 seconds = (aki_get_timestamp() / 1000000.0 - created->timestamp);
- s64 minutes = seconds / 60L;
- s64 hours = minutes / 60L;
- s64 days = hours / 24L;
- if (days > 0) {
- al_sprintf(out, "%lud", days);
- } else if (hours > 0) {
- al_sprintf(out, "%luh", hours);
- } else if (minutes > 0) {
- al_sprintf(out, "%lum", minutes);
- } else {
- al_sprintf(out, "%lus", (s64)seconds);
- }
-}
-
-static void number_to_shorthand(s64 number, char *out)
-{
- if (number >= 1000000) {
- al_sprintf(out, "%.1fM", number / 1000000.f);
- } else if (number >= 1000) {
- al_sprintf(out, "%.1fK", number / 1000.f);
- } else {
- al_sprintf(out, "%ld", number);
- }
-}
-
#define TEXT_OFFSET 10
#define PROFILE_SIZE 5
@@ -259,7 +238,7 @@ static u32 layout_tweet(struct cmc_tui *tui, struct cmc_view_twitter *view, u32
}
view->preview.width = view->text.width - 1;
- view->preview.x = TEXT_OFFSET + 1;
+ view->preview.x = TEXT_OFFSET;
u32 partition_width = view->preview.width / 2;
struct ncplane_options opts = { 0 };
@@ -318,15 +297,48 @@ static u32 layout_tweet(struct cmc_tui *tui, struct cmc_view_twitter *view, u32
return rows + view->preview.height;
}
-static char format_buf[32];
+static void date_to_relative_date(struct sho_post *post, char *out)
+{
+ struct sho_post_date *created = NULL;
+ struct sho_post_date *date;
+ al_array_foreach_ptr(post->dates, i, date) {
+ if (date->type == SHOKI_DATE_CREATED) {
+ created = date;
+ break;
+ }
+ }
+ if (!created) return;
+ f64 seconds = (aki_get_timestamp() / 1000000.0 - created->timestamp);
+ s64 minutes = seconds / 60L;
+ s64 hours = minutes / 60L;
+ s64 days = hours / 24L;
+ if (days > 0) {
+ al_sprintf(out, "%lud", days);
+ } else if (hours > 0) {
+ al_sprintf(out, "%luh", hours);
+ } else if (minutes > 0) {
+ al_sprintf(out, "%lum", minutes);
+ } else {
+ al_sprintf(out, "%lus", (s64)seconds);
+ }
+}
-static void draw_tweet(struct cmc_tui *tui, struct cmc_view_twitter *view, struct ncplane *container, u32 rows, u32 width)
+static void number_to_shorthand(s64 number, char *out)
{
- struct sho_post *post = view->v.post;
- struct sho_post *repost = view->repost;
+ if (number >= 1000000000) {
+ al_sprintf(out, "%.1fB", number / 1000000000.f);
+ } else if (number >= 1000000) {
+ al_sprintf(out, "%.1fM", number / 1000000.f);
+ } else if (number >= 1000) {
+ al_sprintf(out, "%.1fK", number / 1000.f);
+ } else {
+ al_sprintf(out, "%ld", number);
+ }
+}
- ncplane_resize_simple(container, rows, width);
+static void draw_tweet_resources(struct cmc_tui *tui, struct cmc_view_twitter *view)
+{
u32 partition_width = view->preview.width / 2;
struct cmc_view_resource *resource;
switch (view->preview.mode) {
@@ -364,7 +376,7 @@ static void draw_tweet(struct cmc_tui *tui, struct cmc_view_twitter *view, struc
if (view->preview.resources[3]) {
resource = view->preview.resources[3];
ncplane_resize_simple(view->preview.p[3], resource->geom.rcelly + 1, partition_width);
- ncplane_move_yx(view->preview.p[3], view->preview.resources[2]->geom.rcelly + view->text.height + 2, (view->preview.x + view->preview.resources[2]->geom.rcellx) + 3);
+ ncplane_move_yx(view->preview.p[3], view->preview.resources[1]->geom.rcelly + view->text.height + 2, (view->preview.x + view->preview.resources[2]->geom.rcellx) + 3);
resource->vopts.n = view->preview.p[3];
ncvisual_blit(tui->nc, resource->v, &resource->vopts);
}
@@ -373,6 +385,24 @@ static void draw_tweet(struct cmc_tui *tui, struct cmc_view_twitter *view, struc
}
}
+ if (view->profile.resource && view->profile.resource->v) {
+ struct cmc_view_resource *resource = view->profile.resource;
+ ncplane_resize_simple(view->profile.p, (PROFILE_SIZE + 1) / 2, PROFILE_SIZE);
+ ncplane_move_yx(view->profile.p, view->text.y, PROFILE_SIZE - 2);
+ resource->vopts.n = view->profile.p;
+ ncvisual_blit(tui->nc, resource->v, &resource->vopts);
+ }
+}
+
+static char format_buf[32];
+
+static void draw_tweet(struct cmc_view_twitter *view, struct ncplane *container, u32 rows, u32 width)
+{
+ struct sho_post *post = view->v.post;
+ struct sho_post *repost = view->repost;
+
+ ncplane_resize_simple(container, rows, width);
+
ncplane_resize_simple(view->text.p, view->text.height, width);
ncplane_move_yx(view->text.p, 0, 0);
@@ -419,7 +449,7 @@ static void draw_tweet(struct cmc_tui *tui, struct cmc_view_twitter *view, struc
ncplane_move_yx(view->stats.p, rows - 1, TEXT_OFFSET);
u32 part = 10;
- offset = 1;
+ offset = 0;
if (post->comments.set) {
const wchar_t *comment = L"🗩";
@@ -450,14 +480,6 @@ static void draw_tweet(struct cmc_tui *tui, struct cmc_view_twitter *view, struc
number_to_shorthand(post->likes.i, format_buf);
ncplane_putstr_yx(view->stats.p, 0, offset + 2, format_buf);
}
-
- if (view->profile.resource && view->profile.resource->v) {
- struct cmc_view_resource *resource = view->profile.resource;
- ncplane_resize_simple(view->profile.p, (PROFILE_SIZE + 1) / 2, PROFILE_SIZE);
- ncplane_move_yx(view->profile.p, view->text.y, PROFILE_SIZE - 2);
- resource->vopts.n = view->profile.p;
- ncvisual_blit(tui->nc, resource->v, &resource->vopts);
- }
}
static struct cmc_view_page *get_page_from_num(struct cmc_search *search, u32 num)
@@ -477,18 +499,22 @@ static struct cmc_view_page *get_page_containing_index(struct cmc_search *search
if (!(page = get_page_from_num(search, i))) continue;
size = page->views.size;
count += size;
- if (count > index) break;
+ if (count > index) {
+ if (offset) *offset = size - (count - index);
+ return page;
+ }
}
- if (offset) *offset = size - (count - index);
- return page;
+ return NULL;
}
static void request_page_internal(struct cmc *c, u32 num)
{
- c->pending_requests++;
- camu_client_get_page(&c->client, c->tui.search->search, num);
+ struct cmc_search *search = c->tui.search;
+ search->pending_requests++;
+ camu_client_get_page(&c->client, search->search, num);
}
+// Given an index of a post, request the page to the offset of the page containing that post.
static bool request_page_at_page_offset(struct cmc *c, struct cmc_search *search, u32 index, s32 offset)
{
struct cmc_view_page *page = get_page_containing_index(search, index, NULL);
@@ -500,190 +526,283 @@ static bool request_page_at_page_offset(struct cmc *c, struct cmc_search *search
return true;
}
-static bool iterate_views_to_height(struct cmc_search *search, u32 height, u32 anchor,
- u32 *count, void (*func)(void *, struct cmc_view_twitter *, u32), void *userdata)
+static bool iterate_views_to_height(struct cmc_search *search, u32 height, u32 anchor, u32 *count,
+ void (*func)(void *, struct cmc_view_twitter *, u32), void *userdata)
{
u32 y = 0, offset;
- bool out_of_space = false;
struct cmc_view_page *page = get_page_containing_index(search, anchor, &offset);
al_assert(page);
struct cmc_view_twitter *view;
do {
+ if (!page->processed) {
+ // If the page is not processed, return out of space as to not display
+ // a discontinuity (if the page after happens to be processed).
+ return true;
+ }
for (; offset < page->views.size; offset++) {
view = (struct cmc_view_twitter *)al_array_at(page->views, offset);
- if (y + view->v.rows > height) {
- out_of_space = true;
- break;
- }
+ // If this post would draw past the height, return out of space.
+ if (y + view->v.rows > height) return true;
if (func) func(userdata, view, y);
- y += view->v.rows + 1;
(*count)++;
+ y += view->v.rows + 1;
}
- offset = 0;
- } while (!out_of_space && (page = get_page_from_num(search, page->num + 1)));
- return out_of_space;
+ } while ((offset = 0, (page = get_page_from_num(search, page->num + 1))));
+ return false;
}
static void draw_tweet_func(void *userdata, struct cmc_view_twitter *view, u32 y)
{
struct cmc_tui *tui = (struct cmc_tui *)userdata;
- draw_tweet(tui, view, view->v.container, view->v.rows, tui->cols);
+ struct cmc_search *search = tui->search;
ncplane_reparent_family(view->v.container, tui->p);
ncplane_move_yx(view->v.container, y, 0);
+ draw_tweet_resources(tui, view);
view->v.y = y;
+ al_array_push(search->prev, &view->v);
}
-static void draw_selection_line(struct cmc_view_twitter *view, u32 y, u32 rows)
+static void draw_selection_line(struct cmc_view *view, u32 y, u32 rows)
{
- ncplane_move_yx(view->v.container, y, 0);
+ ncplane_move_yx(view->container, y, 0);
wchar_t box = L'â–ˆ';
- u32 k = view->repost ? 1 : 0;
+ u32 k = ((struct cmc_view_twitter *)view)->repost ? 1 : 0;
for (; k < rows - 1; k++) {
- ncplane_cursor_move_yx(view->v.container, k, 0);
- ncplane_putwc(view->v.container, box);
+ ncplane_cursor_move_yx(view->container, k, 0);
+ ncplane_putwc(view->container, box);
}
}
-static struct cmc_view_twitter *get_view_by_visual_index(struct cmc_search *search, s32 index)
+static struct cmc_view *get_view_by_visual_index(struct cmc_search *search, s32 index)
{
u32 offset;
struct cmc_view_page *page = get_page_containing_index(search, search->anchor + index, &offset);
- if (!page) return NULL;
- return (struct cmc_view_twitter *)al_array_at(page->views, offset);
+ if (!page || !page->processed) return NULL;
+ return al_array_at(page->views, offset);
}
-static void layout_tweets(struct cmc *c, struct cmc_tui *tui, struct cmc_search *search)
+static void draw_overlay(struct cmc_tui *tui)
{
- struct cmc_view_page *page;
- struct cmc_view_twitter *view;
- al_array_foreach(search->pages, i, page) {
- if (page->processed) continue;
- for (u32 j = 0; j < page->views.size; j++) {
- view = (struct cmc_view_twitter *)al_array_at(page->views, j);
- view->v.rows = layout_tweet(tui, view, tui->cols);
+ ncplane_cursor_move_yx(tui->overlay.p, 0, 0);
+ ncplane_erase(tui->overlay.p);
+
+ u64 color = 0;
+ u32 background = 0;
+ ncchannel_set_rgb8(&background, 255, 255, 255);
+ ncchannel_set_alpha(&background, 255);
+ ncchannels_set_fchannel(&color, background);
+
+ /*
+ struct nccell ul, ur; nccell_init(&ul); nccell_init(&ur);
+ struct nccell ll, lr; nccell_init(&ll); nccell_init(&lr);
+ struct nccell hl, vl; nccell_init(&hl); nccell_init(&vl);
+ //nccell_set_channels(&hl, color);
+ //nccell_set_channels(&vl, color);
+
+ ncplane_box(tui->overlay.p, &ul, &ur, &ll, &lr, &hl, &vl, tui->overlay.rows - 1, tui->overlay.cols - 1, 0);
+
+ nccell_release(tui->overlay.p, &ul); nccell_release(tui->overlay.p, &ur);
+ nccell_release(tui->overlay.p, &ll); nccell_release(tui->overlay.p, &lr);
+ nccell_release(tui->overlay.p, &hl); nccell_release(tui->overlay.p, &vl);
+ */
+
+ //ncplane_double_box(tui->overlay.p, NCSTYLE_NONE, color, tui->overlay.rows - 1, tui->overlay.cols - 1, 0);
+ //ncplane_rounded_box(tui->overlay.p, NCSTYLE_NONE, color, tui->overlay.rows - 1, tui->overlay.cols - 1, 0);
+
+ //ncchannel_set_palindex(&background, 1);
+ ncchannel_set_rgb8(&background, 124, 12, 53);
+ ncchannels_set_bchannel(&color, background);
+ ncchannels_set_fchannel(&color, background);
+ ncplane_gradient(tui->overlay.p, 0, 0, tui->overlay.rows, tui->overlay.cols, "", 0, color, color, color, color);
+
+ ncplane_set_bg_rgb8(tui->overlay.p, 124, 12, 53);
+ //ncplane_set_fg_palindex(tui->overlay.p, 4);
+ //ncplane_set_bg_alpha(tui->overlay.p, 0);
+ //ncplane_cursor_move_yx(tui->overlay.p, 1, 1);
+ //ncplane_cursor_move_yx(tui->overlay.p, 0, 1);
+
+ struct cmc_search *search;
+ al_array_foreach(tui->searches, i, search) {
+ if ((s32)i == tui->overlay.selected) {
+ ncplane_set_fg_palindex(tui->overlay.p, 4);
+ } else {
+ ncplane_set_fg_default(tui->overlay.p);
+ }
+ str *query = &search->search->query;
+ for (u32 j = 0; j < query->len; j++) {
+ ncplane_putchar_yx(tui->overlay.p, i, j + 1, al_str_at(query, j));
}
- page->processed = true;
}
+}
- if (search->find_anchor) {
+static void refresh_pane_search(struct cmc *c, struct cmc_tui *tui, struct cmc_search *search)
+{
+ if (search->find_anchor == SEARCH_UP) {
+ u32 count;
+ // If find_anchor = SEARCH_UP, search->anchor is index of the requested view.
s32 anchor = search->anchor;
- if (anchor < 0) {
- if (!request_page_at_page_offset(c, search, 0, -1)) {
- search->anchor = 0;
- search->selected = 0;
- search->find_anchor = false;
- }
- return;
- }
- u32 count = 1, prev_count;
while (anchor >= 0) {
count = 0;
iterate_views_to_height(search, tui->rows, anchor, &count, NULL, NULL);
+ // Is the requested view still visible?
if ((s32)(anchor + count) - 1 < search->anchor) break;
anchor--;
- if (anchor < 0 && request_page_at_page_offset(c, search, 0, -1)) {
- return;
- }
- prev_count = count;
+ // Load more posts if needed.
+ if (anchor < 0 && request_page_at_page_offset(c, search, 0, -1)) return;
}
- search->anchor = anchor + 1;
- search->prev_selected = -1;
- search->selected = prev_count - 1;
- search->find_anchor = false;
+ anchor++; // We want the anchor before the last attempt above.
+ search->selected = abs(search->anchor - anchor);
+ search->anchor = anchor;
+ search->redraw = true;
+ search->find_anchor = SEARCH_NONE;
+ } else if (search->find_anchor == SEARCH_DOWN) {
+ // When moving down we already know how many views are being displayed
+ // so just move the anchor 1 past that.
+ search->selected = 0;
+ search->anchor += search->count;
+ search->redraw = true;
+ search->find_anchor = SEARCH_NONE;
}
+ if (tui->overlay.active) draw_overlay(tui);
+
+ struct cmc_view *view;
bool out_of_space = true;
- if (search->prev_anchor == -1 || search->anchor != search->prev_anchor) {
- al_array_foreach(search->pages, i, page) {
- for (u32 j = 0; j < page->views.size; j++) {
- view = (struct cmc_view_twitter *)al_array_at(page->views, j);
- ncplane_erase(view->v.container);
- ncplane_reparent_family(view->v.container, view->v.container);
- }
+ if (search->redraw) {
+ al_array_foreach(search->prev, j, view) {
+ ncplane_erase(view->container);
+ ncplane_reparent_family(view->container, view->container);
}
+ search->prev.size = 0;
search->count = 0;
out_of_space = iterate_views_to_height(search, tui->rows, search->anchor, &search->count, draw_tweet_func, tui);
- notcurses_refresh(tui->nc, NULL, NULL);
- } else if (search->prev_selected >= 0) {
- view = get_view_by_visual_index(search, search->prev_selected);
- if (view) {
- ncplane_erase(view->v.container);
+ } else {
+ al_array_foreach(search->prev, j, view) {
+ ncplane_erase(view->container);
}
}
- view = get_view_by_visual_index(search, search->selected);
- if (view) {
- draw_selection_line(view, view->v.y, view->v.rows);
+ if ((view = get_view_by_visual_index(search, search->selected))) {
+ draw_selection_line(view, view->y, view->rows);
+ }
+ if (search->redraw) {
+ search->redraw = false;
+ notcurses_refresh(tui->nc, NULL, NULL);
}
notcurses_render(tui->nc);
- if (search->count > 0 && !out_of_space && search->end_of_page == -1) {
- // Index is the last displayed view.
+ if (search->pending_requests == 0 && search->count > 0 && !out_of_space && search->end_of_page == -1) {
request_page_at_page_offset(c, search, (search->anchor + search->count) - 1, 1);
- search->prev_anchor = -1;
- } else {
- search->prev_anchor = search->anchor;
+ }
+}
+
+static void maybe_process_pages(struct cmc *c)
+{
+ struct cmc_tui *tui = &c->tui;
+ bool do_redraw = false;
+ struct cmc_search *search;
+ al_array_foreach(tui->searches, i, search) {
+ struct cmc_view_page *page;
+ al_array_foreach(search->pages, j, page) {
+ if (page->processed) continue;
+ struct cmc_view *view;
+ bool any_pending_requests = false;
+ al_array_foreach(page->views, k, view) {
+ if (view->pending_requests > 0) {
+ any_pending_requests = true;
+ break;
+ }
+ }
+ if (any_pending_requests) continue;
+ do_redraw = true;
+ al_array_foreach(page->views, k, view) {
+ view->rows = layout_tweet(tui, (struct cmc_view_twitter *)view, tui->cols);
+ draw_tweet((struct cmc_view_twitter *)view, view->container, view->rows, tui->cols);
+ }
+ page->processed = true;
+ }
+ }
+ search = tui->search;
+ if (do_redraw) {
+ search->redraw = true;
+ refresh_pane_search(c, tui, search);
}
}
static void resource_client_callback(void *userdata, u16 id, struct aki_buffer *buffer)
{
struct cmc *c = (struct cmc *)userdata;
- struct cmc_tui *tui = &c->tui;
struct cmc_view_resource *resource;
al_array_foreach(c->resource_cache, i, resource) {
if (resource->id == id) {
s32 w, h, channels;
- u8 *pixels = stbi_load_from_memory(aki_buffer_get_ptr(buffer, 0), buffer->size, &w, &h, &channels, 4);
- resource->v = ncvisual_from_rgba(pixels, h, w * 4, w);
+ u8 *pixels = stbi_load_from_memory(aki_buffer_get_ptr(buffer, 0), buffer->size, &w, &h, &channels, 0);
+ if (channels == 3) {
+ resource->v = ncvisual_from_rgb_packed(pixels, h, w * channels, w, 255);
+ } else if (channels == 4) {
+ resource->v = ncvisual_from_rgba(pixels, h, w * channels, w);
+ } else {
+ al_log_warn("cmc", "Image has unsupported channel count.");
+ }
stbi_image_free(pixels);
ncvisual_geom(c->tui.nc, resource->v, &resource->vopts, &resource->geom);
+ struct cmc_view *view;
+ al_array_foreach(resource->refs, j, view) {
+ view->pending_requests--;
+ }
+ al_array_free(resource->refs);
break;
}
}
- if (--c->pending_requests == 0) layout_tweets(c, tui, tui->search);
+ maybe_process_pages(c);
}
static void add_selected_from_index(struct cmc *c, struct cmc_search *search, u32 index)
{
- struct cmc_view_twitter *view = get_view_by_visual_index(search, search->selected);
- if (view && view->v.post->media.size > index) {
- camu_client_add(&c->client, &view->v.post->unique_id, index);
+ struct cmc_view *view = get_view_by_visual_index(search, search->selected);
+ if (view && view->post->media.size > index) {
+ camu_client_add(&c->client, &view->post->unique_id, index);
camu_client_skip(&c->client, 1);
}
}
-static bool on_last_page(struct cmc_search *search)
-{
- return search->end_of_page >= 0 && search->anchor + search->count > (u32)search->end_of_page;
-}
+#define ON_LAST_PAGE(search) \
+ (search->end_of_page >= 0 && search->anchor + search->count > (u32)search->end_of_page)
static bool handle_input_search(struct cmc *c, struct cmc_search *search, struct ncinput *input)
{
if (!(input->evtype == NCTYPE_PRESS || input->evtype == NCTYPE_UNKNOWN)) return false;
if (input->id == 'j') {
if (search->selected + 1 >= (s32)search->count) {
- if (c->pending_requests != 0 || on_last_page(search)) return false;
- search->anchor += search->count;
- search->prev_selected = -1;
- search->selected = 0;
+ if (search->pending_requests > 0 || search->find_anchor != SEARCH_NONE) return false;
+ if (ON_LAST_PAGE(search)) return false;
+ u32 index = search->anchor + search->count;
+ search->find_anchor = SEARCH_DOWN;
+ struct cmc_view_page *page = get_page_containing_index(search, index, NULL);
+ if (!page) {
+ request_page_at_page_offset(c, search, index - 1, 1);
+ return false;
+ }
} else {
- search->prev_selected = search->selected;
search->selected++;
}
return true;
} else if (input->id == 'k') {
if (search->selected - 1 < 0) {
- if (c->pending_requests != 0) return false;
+ if (search->pending_requests > 0 || search->find_anchor != SEARCH_NONE) return false;
+ if (search->anchor - 1 < 0 && !request_page_at_page_offset(c, search, 0, -1)) {
+ // The reason to do this here instead of letting it resolve in
+ // refresh_pane_search() is so we can return early if
+ // we are at the top of the page.
+ return false;
+ }
search->anchor--;
- search->find_anchor = true;
- search->prev_selected = -1;
+ search->find_anchor = SEARCH_UP;
+ // Wait for the requested previous page to come in.
+ if (search->anchor < 0) return false;
} else {
- search->prev_selected = search->selected;
search->selected--;
}
return true;
- } else if (input->id == NCKEY_RETURN) {
- add_selected_from_index(c, search, 0);
- } else if (input->id == '1') {
+ } else if (input->id == NCKEY_RETURN || input->id == '1') {
add_selected_from_index(c, search, 0);
} else if (input->id == '2') {
add_selected_from_index(c, search, 1);
@@ -695,15 +814,60 @@ static bool handle_input_search(struct cmc *c, struct cmc_search *search, struct
return false;
}
+static void toggle_overlay_internal(struct cmc_tui *tui)
+{
+ if (tui->overlay.active) {
+ ncplane_reparent_family(tui->overlay.p, tui->overlay.p);
+ } else {
+ ncplane_reparent_family(tui->overlay.p, tui->p);
+ }
+ tui->overlay.active = !tui->overlay.active;
+}
+
+static bool handle_input_overlay(struct cmc *c, struct cmc_tui *tui, struct ncinput *input)
+{
+ if (input->evtype == NCTYPE_PRESS || input->evtype == NCTYPE_UNKNOWN) {
+ if (input->id == 'j') {
+ if (tui->overlay.selected + 1 < (s32)tui->searches.size) {
+ tui->overlay.selected++;
+ return true;
+ }
+ } else if (input->id == 'k') {
+ if (tui->overlay.selected - 1 >= 0) {
+ tui->overlay.selected--;
+ return true;
+ }
+ } else if (input->id == NCKEY_RETURN) {
+ toggle_overlay_internal(tui);
+ struct cmc_search *search = tui->search;
+ struct cmc_view *view;
+ al_array_foreach(search->prev, j, view) {
+ ncplane_erase(view->container);
+ ncplane_reparent_family(view->container, view->container);
+ }
+ search->prev.size = 0;
+ tui->search = al_array_at(tui->searches, tui->overlay.selected);
+ search = tui->search;
+ search->redraw = true;
+ u32 last_page = search->search->last_page;
+ if (!get_page_from_num(search, last_page)) {
+ request_page_internal(c, last_page);
+ } else {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
static void input_poll_callback(void *userdata, s32 revents)
{
struct cmc *c = (struct cmc *)userdata;
struct cmc_tui *tui = &c->tui;
- struct cmc_search *search = tui->search;
(void)revents;
struct ncinput input;
u32 ret;
- bool do_layout = false;
+ bool do_refresh = false;
do {
ret = notcurses_get_nblock(c->tui.nc, &input);
if (ret == (u32)-1 || ret == 0) break;
@@ -711,11 +875,17 @@ static void input_poll_callback(void *userdata, s32 revents)
if (input.id == 'q') {
aki_event_loop_break(&c->loop);
break;
+ } else if (input.id == NCKEY_TAB) {
+ toggle_overlay_internal(tui);
+ do_refresh = true;
+ tui->search->redraw = true;
+ continue;
}
- if (search) do_layout = handle_input_search(c, search, &input);
+ if (tui->overlay.active) do_refresh |= handle_input_overlay(c, tui, &input);
+ else if (tui->search) do_refresh |= handle_input_search(c, tui->search, &input);
}
} while (1);
- if (do_layout && c->pending_requests == 0) layout_tweets(c, tui, search);
+ if (do_refresh) refresh_pane_search(c, tui, tui->search);
}
static struct cmc_view_resource *create_resource(struct cmc *c, str *unique_id, u32 index)
@@ -727,12 +897,13 @@ static struct cmc_view_resource *create_resource(struct cmc *c, str *unique_id,
resource->id = al_rand_u16();
al_str_clone(&resource->unique_id, unique_id);
resource->index = index;
- c->pending_requests++;
+ al_array_init(resource->refs);
camu_resource_request(&c->resource_client, unique_id, index, resource->id);
return resource;
}
-static struct cmc_view_resource *get_resource_from_cache(struct cmc *c, str *unique_id, u32 index)
+static struct cmc_view_resource *get_resource_from_cache(struct cmc *c, struct cmc_view *view,
+ str *unique_id, u32 index)
{
struct cmc_view_resource *resource;
al_array_foreach(c->resource_cache, i, resource) {
@@ -740,7 +911,10 @@ static struct cmc_view_resource *get_resource_from_cache(struct cmc *c, str *uni
return resource;
}
}
- return create_resource(c, unique_id, index);
+ resource = create_resource(c, unique_id, index);
+ view->pending_requests++;
+ al_array_push(resource->refs, view);
+ return resource;
}
static void get_post_and_maybe_repost(struct cmc *c, str *unique_id, struct sho_post **post, struct sho_post **repost)
@@ -753,11 +927,31 @@ static void get_post_and_maybe_repost(struct cmc *c, str *unique_id, struct sho_
}
}
+static void strip_link_from_tweet(struct sho_post *post)
+{
+ wstr *check = al_wstr_c(L"https://t.co/");
+ s32 index = al_wstr_rfind(&post->text, L':');
+ if (index < 5) return;
+ index -= 5; // [h]ttps:// (if matched).
+ if (al_wstr_cmp(&post->text, check, index, check->len) == 0) {
+ al_wstr_at(&post->text, index) = L'\0';
+ post->text.len = index;
+ }
+}
+
static struct cmc_view_twitter *create_view_twitter(struct cmc *c, str *unique_id)
{
struct cmc_view_twitter *view = al_alloc_object(struct cmc_view_twitter);
get_post_and_maybe_repost(c, unique_id, &view->v.post, &view->repost);
+ if (view->v.post->media.size > 0) {
+ strip_link_from_tweet(view->v.post);
+ }
+
+ view->v.rows = 0;
+ view->v.y = 0;
+ view->v.pending_requests = 0;
+
struct ncplane_options default_options = { 0 };
default_options.cols = 1;
default_options.rows = 1;
@@ -773,11 +967,11 @@ static struct cmc_view_twitter *create_view_twitter(struct cmc *c, str *unique_i
view->stats.p = ncplane_create(view->v.container, &default_options);
struct sho_post *post = view->v.post;
- view->profile.resource = get_resource_from_cache(c, &post->author.unique_id, 0);
+ view->profile.resource = get_resource_from_cache(c, &view->v, &post->author.unique_id, 0);
struct sho_post_media *media;
al_array_foreach_ptr(post->media, i, media) {
(void)media;
- view->preview.resources[i] = get_resource_from_cache(c, &post->unique_id, i);
+ view->preview.resources[i] = get_resource_from_cache(c, &view->v, &post->unique_id, i);
}
switch (post->media.size) {
@@ -792,9 +986,6 @@ static struct cmc_view_twitter *create_view_twitter(struct cmc *c, str *unique_i
break;
}
- view->v.rows = 0;
- view->v.y = 0;
-
return view;
}
@@ -802,14 +993,15 @@ static struct cmc_search *create_search(struct camu_search *s)
{
struct cmc_search *search = al_alloc_object(struct cmc_search);
search->search = s;
- search->find_anchor = false;
+ search->find_anchor = SEARCH_NONE;
search->anchor = 0;
- search->prev_anchor = -1;
+ search->redraw = true;
search->selected = 0;
- search->prev_selected = -1;
+ al_array_init(search->prev);
search->max_page_num = s->last_page;
search->min_page_num = s->last_page;
search->end_of_page = -1;
+ search->pending_requests = 0;
al_array_init(search->pages);
return search;
}
@@ -826,10 +1018,24 @@ static s32 resize_cb(struct ncplane *p)
nopts.y = 1;
nopts.flags = 0;
tui->p = ncplane_create(p, &nopts);
- // TODO: Consider layout.
+ tui->overlay.cols = nopts.cols = 35;
+ tui->overlay.rows = nopts.rows = 20;
+ nopts.x = (tui->cols / 2) - (tui->overlay.cols / 2);
+ nopts.y = (tui->rows / 2) - (tui->overlay.rows / 2);
+ tui->overlay.p = ncpile_create(tui->nc, &nopts);
+ //ncplane_reparent_family(tui->overlay.p, tui->p);
return 0;
}
+static struct cmc_search *get_search_from_id(struct cmc_tui *tui, s32 id)
+{
+ struct cmc_search *search;
+ al_array_foreach(tui->searches, i, search) {
+ if (search->search->id == id) return search;
+ }
+ return NULL;
+}
+
static void client_callback(void *userdata, u8 op, void *opaque)
{
struct cmc *c = (struct cmc *)userdata;
@@ -854,6 +1060,8 @@ static void client_callback(void *userdata, u8 op, void *opaque)
} else {
tui->search = NULL;
}
+ tui->overlay.active = false;
+ tui->overlay.selected = 0;
struct ncplane *stdplane = notcurses_stdplane(tui->nc);
ncplane_set_resizecb(stdplane, resize_cb);
ncplane_set_userptr(stdplane, tui);
@@ -897,8 +1105,10 @@ static void client_callback(void *userdata, u8 op, void *opaque)
break;
}
case CAMU_CLIENT_RESULTS: {
- struct cmc_search *search = tui->search; // wrong
struct camu_result_list *results = (struct camu_result_list *)opaque;
+ struct cmc_search *search = get_search_from_id(tui, results->search->id);
+ if (!search) return;
+ search->pending_requests--;
struct cmc_view_page *page = al_alloc_object(struct cmc_view_page);
al_array_push(search->pages, page);
@@ -906,7 +1116,7 @@ static void client_callback(void *userdata, u8 op, void *opaque)
page->processed = false;
if (page->num > search->max_page_num) search->max_page_num = page->num;
if (page->num < search->min_page_num) search->min_page_num = page->num;
- if (search->find_anchor) {
+ if (search->find_anchor == SEARCH_UP) {
search->anchor += results->unique_ids.size;
if (search->end_of_page >= 0) {
search->end_of_page += results->unique_ids.size;
@@ -917,17 +1127,19 @@ static void client_callback(void *userdata, u8 op, void *opaque)
struct cmc_view_twitter *view;
al_array_foreach_ptr(results->unique_ids, i, unique_id) {
view = create_view_twitter(c, unique_id);
- al_array_push(page->views, (struct cmc_view *)view);
+ al_array_push(page->views, &view->v);
}
- if (--c->pending_requests == 0) layout_tweets(c, tui, search);
+ maybe_process_pages(c);
break;
}
case CAMU_CLIENT_END_OF_RESULTS: {
- struct cmc_search *search = tui->search; // wrong
+ struct cmc_search *search = get_search_from_id(tui, *((s32 *)opaque));
+ if (!search) return;
+ search->pending_requests--;
search->end_of_page = (search->anchor + search->count) - 1;
- if (--c->pending_requests == 0) layout_tweets(c, tui, search);
+ maybe_process_pages(c);
break;
}
case CAMU_CLIENT_DISCONNECTED:
@@ -984,9 +1196,7 @@ s32 main(s32 argc, char *argv[])
if (c.type == CMC_SEARCH) {
camu_client_create_search(&c.client, &module, &query);
}
- }
-
- if (c.action == CMC_ACTION_OPEN) {
+ } else if (c.action == CMC_ACTION_OPEN) {
c.pending_requests = 0;
camu_resource_client_init(&c.resource_client, &c.loop, resource_client_callback, &c);
camu_resource_client_connect(&c.resource_client, TREE_SERVER_IP, TREE_RESOURCE_PORT);
diff --git a/src/libclient/client.c b/src/libclient/client.c
index 71825e3..1194994 100644
--- a/src/libclient/client.c
+++ b/src/libclient/client.c
@@ -31,9 +31,6 @@ static void connection_closed_callback(void *userdata, struct aki_rpc_connection
static u8 packet_pool_callback(void *userdata, struct aki_packet *packet)
{
struct camu_client *client = (struct camu_client *)userdata;
- while (!client->conn) {
- aki_event_loop_run_once(client->loop);
- }
aki_rpc_connection_command(client->conn, packet, packet->userdata, client);
return AKI_PACKET_POOL_NOP;
}
@@ -112,6 +109,10 @@ bool camu_client_login(struct camu_client *client, str *username, str *addr, s32
if (!aki_rpc_connect(&client->client, client->loop, addr, port)) {
return false;
}
+ while (!client->conn) {
+ aki_event_loop_run_once(client->loop);
+ }
+ // TODO: Timeout.
return true;
}
@@ -144,29 +145,31 @@ static void results_callback(void *userdata, struct aki_packet *packet)
{
struct camu_client *client = (struct camu_client *)userdata;
+ s32 id = aki_packet_read_s32(packet);
+
s32 ok = aki_packet_read_s32(packet);
if (ok != 0) {
- client->callback(client->userdata, CAMU_CLIENT_END_OF_RESULTS, NULL);
+ client->callback(client->userdata, CAMU_CLIENT_END_OF_RESULTS, &id);
return;
}
- s32 id = aki_packet_read_s32(packet);
struct camu_search *search = get_search_from_id(client, id);
if (!search) return;
u32 page_num = aki_packet_read_u32(packet);
- struct camu_result_list *list;
- al_array_foreach(search->lists, i, list) {
- if (list->num == page_num) {
+ struct camu_result_list *results;
+ al_array_foreach(search->lists, i, results) {
+ if (results->num == page_num) {
al_log_warn("client", "Requested page was already loaded.");
return;
}
}
- list = al_alloc_object(struct camu_result_list);
- al_array_push(search->lists, list);
- list->num = page_num;
- al_array_init(list->unique_ids);
+ results = al_alloc_object(struct camu_result_list);
+ al_array_push(search->lists, results);
+ results->num = page_num;
+ al_array_init(results->unique_ids);
+ results->search = search;
u32 size = aki_packet_read_u32(packet);
for (u32 i = 0; i < size; i++) {
@@ -181,10 +184,10 @@ static void results_callback(void *userdata, struct aki_packet *packet)
for (u32 i = 0; i < size; i++) {
aki_packet_read_str(packet, &s);
al_str_clone(&unique_id, &s);
- al_array_push(list->unique_ids, unique_id);
+ al_array_push(results->unique_ids, unique_id);
}
- client->callback(client->userdata, CAMU_CLIENT_RESULTS, list);
+ client->callback(client->userdata, CAMU_CLIENT_RESULTS, results);
}
void camu_client_get_page(struct camu_client *client, struct camu_search *search, u32 num)
diff --git a/src/libclient/client.h b/src/libclient/client.h
index 2b2e093..edb7881 100644
--- a/src/libclient/client.h
+++ b/src/libclient/client.h
@@ -23,8 +23,8 @@ struct camu_list {
struct camu_result_list {
u32 num;
- s32 search_id;
array(str) unique_ids;
+ struct camu_search *search;
};
struct camu_search {
diff --git a/src/libclient/resource_client.c b/src/libclient/resource_client.c
index 1e07b0b..0318305 100644
--- a/src/libclient/resource_client.c
+++ b/src/libclient/resource_client.c
@@ -48,6 +48,7 @@ static void signal_callback(void *userdata)
aki_packet_write_str(packet, &request.unique_id);
aki_packet_write_u32(packet, request.index);
aki_packet_stream_send_packet(&client->client, packet);
+ al_str_free(&request.unique_id);
} while (1);
}
@@ -63,8 +64,7 @@ void camu_resource_client_init(struct camu_resource_client *client,
connection_closed_callback, packet_callback, packet_sent_callback, client);
}
-
-void camu_resource_client_connect(struct camu_resource_client *client, str *addr, s32 port)
+bool camu_resource_client_connect(struct camu_resource_client *client, str *addr, s32 port)
{
aki_signal_start(&client->signal, client->loop);
aki_packet_stream_connect(&client->client, client->loop, addr, port);
@@ -72,10 +72,11 @@ void camu_resource_client_connect(struct camu_resource_client *client, str *addr
while (!client->connected) {
aki_event_loop_run_once(client->loop);
}
+ // TODO: Timeout.
+ return true;
}
-void camu_resource_request(struct camu_resource_client *client,
- str *unique_id, u32 index, u16 id)
+void camu_resource_request(struct camu_resource_client *client, str *unique_id, u32 index, u16 id)
{
struct camu_resource_request request;
request.id = id;
diff --git a/src/libclient/resource_client.h b/src/libclient/resource_client.h
index 17ac31e..755e661 100644
--- a/src/libclient/resource_client.h
+++ b/src/libclient/resource_client.h
@@ -25,6 +25,5 @@ struct camu_resource_client {
void camu_resource_client_init(struct camu_resource_client *client,
struct aki_event_loop *loop, void (*callback)(void *, u16, struct aki_buffer *), void *userdata);
-void camu_resource_client_connect(struct camu_resource_client *client, str *addr, s32 port);
-void camu_resource_request(struct camu_resource_client *client,
- str *unique_id, u32 index, u16 id);
+bool camu_resource_client_connect(struct camu_resource_client *client, str *addr, s32 port);
+void camu_resource_request(struct camu_resource_client *client, str *unique_id, u32 index, u16 id);
diff --git a/src/libsink/sink.c b/src/libsink/sink.c
index 134be31..55485b0 100644
--- a/src/libsink/sink.c
+++ b/src/libsink/sink.c
@@ -245,11 +245,10 @@ void add_audio_if_set_and_buffered(struct camu_sink_entry *entry)
{
u8 state = entry->audio.state;
if (state == BUFFER_SET_OR_BUFFERED) {
- // TODO: should_resume is not robust.
+ // TODO: should_resume is not robust. CAMU_SINK_NO_VIDEO doesn't work but that's
+ // the least of our problems.
#ifndef CAMU_SINK_NO_VIDEO
bool should_resume = ENTRY_VIDEO_EMPTY(entry) || entry->video.state == BUFFER_ADDED;
-#else
- bool should_resume = true;
#endif
if (should_resume && !camu_clock_calc_tick(&entry->clock)) {
queue_cmd(entry->sink, (struct camu_sink_cmd){
@@ -670,6 +669,7 @@ static bool set_command_callback(void *userdata, struct aki_rpc_connection *conn
#endif
if (previous) {
camu_clock_pause(&previous->clock);
+ // TODO: sink->previous that gets swapped in add_x_if_set_and_buffered?
remove_entry_buffers(sink, previous);
}
out:
diff --git a/src/tree/tree.c b/src/tree/tree.c
index 8dcc501..1be4891 100644
--- a/src/tree/tree.c
+++ b/src/tree/tree.c
@@ -5,7 +5,7 @@
#include "tree.h"
#include "common.h"
-#define USER_AGENT al_str_c("Mozilla/5.0 (X11; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0")
+#define USER_AGENT al_str_c("Mozilla/5.0 (Windows NT 10.0; rv:122.0) Gecko/20100101 Firefox/122.0")
static void http_callback(void *userdata, struct aki_http_request *response, bool success)
{
@@ -64,21 +64,16 @@ static struct tree_user *get_user_by_connection(struct tree_server *tree, struct
al_array_foreach(tree->users, i, user) {
struct tree_client *client;
al_array_foreach(user->clients, j, client) {
- if (client->conn == conn) {
- return user;
- }
+ if (client->conn == conn) return user;
}
struct tree_list *list;
al_array_foreach(user->lists, j, list) {
struct tree_sink *sink;
al_array_foreach(list->sinks, k, sink) {
- if (sink->conn == conn) {
- return user;
- }
+ if (sink->conn == conn) return user;
}
}
}
-
return NULL;
}
@@ -173,6 +168,11 @@ out:
static bool get_page_command_callback(void *userdata, struct aki_rpc_connection *conn,
struct aki_packet *packet, struct aki_packet *rpacket)
{
+ s32 id = aki_packet_read_s32(packet);
+
+ // Always reciprocate the requested id, even if it turns out to be invalid.
+ aki_packet_write_s32(rpacket, id);
+
struct tree_server *tree = (struct tree_server *)userdata;
struct tree_user *user = get_user_by_connection(tree, conn);
if (!user) {
@@ -180,7 +180,6 @@ static bool get_page_command_callback(void *userdata, struct aki_rpc_connection
goto out;
}
- s32 id = aki_packet_read_s32(packet);
u32 page_request = aki_packet_read_u32(packet);
struct sho_search *search = sho_client_get_search(&user->search, id);
@@ -195,7 +194,6 @@ static bool get_page_command_callback(void *userdata, struct aki_rpc_connection
}
aki_packet_write_s32(rpacket, 0);
- aki_packet_write_s32(rpacket, search->id);
aki_packet_write_u32(rpacket, page_request);
struct sho_result_page *page = &al_array_at(search->pages, page_request);
aki_packet_write_u32(rpacket, page->posts.size);
@@ -228,7 +226,6 @@ static bool add_command_callback(void *userdata, struct aki_rpc_connection *conn
struct tree_list *list = al_array_at(user->lists, 0);
tree_list_add(list, &unique_id, index);
- tree_list_skip(list, 1);
out:
aki_packet_free(packet);
@@ -314,7 +311,6 @@ static void connection_closed_callback(void *userdata, struct aki_rpc_connection
static struct aki_rpc_command commands[] = {
{ .op = TREE_CMD_IDENTIFY, .callback = identify_command_callback, .userdata = NULL },
- // Client commands.
{ .op = TREE_CMD_CREATE_SEARCH, .callback = create_search_command_callback, .userdata = NULL },
{ .op = TREE_CMD_GET_PAGE, .callback = get_page_command_callback, .userdata = NULL },
{ .op = TREE_CMD_ADD, .callback = add_command_callback, .userdata = NULL },