From 60b4ebfbf3be78dba9dc7c65ab2bdaa0b218c0c2 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Mon, 21 Oct 2024 19:22:50 -0400 Subject: Everything before initial synced list Signed-off-by: Andrew Opalach --- src/fruits/cmc/cmc.c | 128 ------------- src/fruits/cmc/cmc.h | 27 --- src/fruits/cmc/cmc.py | 17 ++ src/fruits/cmc/meson.build | 15 -- src/fruits/cmc/tui/pager.c | 88 --------- src/fruits/cmc/tui/pager.h | 33 ---- src/fruits/cmc/tui/pane_lists.c | 93 ---------- src/fruits/cmc/tui/pane_lists.h | 20 --- src/fruits/cmc/tui/pane_search.c | 335 ---------------------------------- src/fruits/cmc/tui/pane_search.h | 29 --- src/fruits/cmc/tui/resources.c | 85 --------- src/fruits/cmc/tui/resources.h | 31 ---- src/fruits/cmc/tui/ui.c | 144 --------------- src/fruits/cmc/tui/ui.h | 40 ----- src/fruits/cmc/tui/view.h | 24 --- src/fruits/cmc/tui/view_twitter.c | 370 -------------------------------------- src/fruits/cmc/tui/view_twitter.h | 35 ---- src/fruits/cmsrv/cmsrv.c | 196 ++++++++++++++++++++ src/fruits/cmsrv/meson.build | 19 ++ src/fruits/cmsrv/ui.c | 210 ++++++++++++++++++++++ src/fruits/cmsrv/ui.h | 44 +++++ src/fruits/cmv/cmv.c | 151 ++++++++++++++++ src/fruits/cmv/meson.build | 25 +++ src/fruits/sink/meson.build | 15 -- src/fruits/sink/sink.c | 208 --------------------- 25 files changed, 662 insertions(+), 1720 deletions(-) delete mode 100644 src/fruits/cmc/cmc.c delete mode 100644 src/fruits/cmc/cmc.h create mode 100644 src/fruits/cmc/cmc.py delete mode 100644 src/fruits/cmc/meson.build delete mode 100644 src/fruits/cmc/tui/pager.c delete mode 100644 src/fruits/cmc/tui/pager.h delete mode 100644 src/fruits/cmc/tui/pane_lists.c delete mode 100644 src/fruits/cmc/tui/pane_lists.h delete mode 100644 src/fruits/cmc/tui/pane_search.c delete mode 100644 src/fruits/cmc/tui/pane_search.h delete mode 100644 src/fruits/cmc/tui/resources.c delete mode 100644 src/fruits/cmc/tui/resources.h delete mode 100644 src/fruits/cmc/tui/ui.c delete mode 100644 src/fruits/cmc/tui/ui.h delete mode 100644 src/fruits/cmc/tui/view.h delete mode 100644 src/fruits/cmc/tui/view_twitter.c delete mode 100644 src/fruits/cmc/tui/view_twitter.h create mode 100644 src/fruits/cmsrv/cmsrv.c create mode 100644 src/fruits/cmsrv/meson.build create mode 100644 src/fruits/cmsrv/ui.c create mode 100644 src/fruits/cmsrv/ui.h create mode 100644 src/fruits/cmv/cmv.c create mode 100644 src/fruits/cmv/meson.build delete mode 100644 src/fruits/sink/meson.build delete mode 100644 src/fruits/sink/sink.c (limited to 'src/fruits') diff --git a/src/fruits/cmc/cmc.c b/src/fruits/cmc/cmc.c deleted file mode 100644 index 5bbdb0b..0000000 --- a/src/fruits/cmc/cmc.c +++ /dev/null @@ -1,128 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "../../server/common.h" - -#include "cmc.h" - -static void client_callback(void *userdata, u8 op, void *opaque) -{ - struct cmc *c = (struct cmc *)userdata; - switch (op) { - case CAMU_CLIENT_STATE_UPDATED: { - switch (c->action) { - case CMC_ACTION_OPEN: { - cmc_ui_open(&c->ui, c); - break; - } - case CMC_ACTION_LIST: { - switch (c->type) { - case CMC_LIST: { - struct camu_list *list; - al_array_foreach(c->client.state.lists, i, list) { - al_printf("%.*s:\n", AL_STR_PRINTF(&list->name)); - str *unique_id; - al_array_foreach_ptr(list->entries, j, unique_id) { - if ((s32)j == list->current) { - al_printf(" > %.*s\n", AL_STR_PRINTF(unique_id)); - } else { - al_printf(" %.*s\n", AL_STR_PRINTF(unique_id)); - } - } - } - aki_event_loop_break(&c->loop); - break; - } - case CMC_SEARCH: { - struct camu_search *search; - al_array_foreach(c->client.state.searches, i, search) { - al_printf("%u %x %.*s\n", i, search->id, AL_STR_PRINTF(&search->query)); - } - aki_event_loop_break(&c->loop); - break; - } - } - break; - } - } - break; - } - case CAMU_CLIENT_SEARCH_CREATED: { - aki_event_loop_break(&c->loop); - break; - } - case CAMU_CLIENT_RESULTS: { - cmc_ui_handle_results(&c->ui.search, (struct camu_result_list *)opaque); - break; - } - case CAMU_CLIENT_END_OF_RESULTS: { - cmc_ui_handle_end_of_results(&c->ui.search, *((s32 *)opaque)); - break; - } - case CAMU_CLIENT_DISCONNECTED: - aki_event_loop_break(&c->loop); - break; - } -} - -static struct cmc c = { 0 }; - -s32 main(s32 argc, char *argv[]) -{ - if (argc < 2 || !aki_common_init()) return EXIT_FAILURE; - - str module; - str query; - - str *cmd = al_str_cr(argv[1]); - if (al_str_eq(cmd, al_str_c("search"))) { - c.type = CMC_SEARCH; - if (argc == 2) { - c.action = CMC_ACTION_LIST; - } else { - c.action = CMC_ACTION_CREATE; - al_str_from(&module, "twitter"); - al_str_from(&query, argv[2]); - } - } else if (al_str_eq(cmd, al_str_c("open"))) { - c.action = CMC_ACTION_OPEN; - } else if (al_str_eq(cmd, al_str_c("list"))) { - c.type = CMC_LIST; - if (argc == 2) { - c.action = CMC_ACTION_LIST; - } else { - c.action = CMC_ACTION_CREATE; - } - } else { - return EXIT_FAILURE; - } - - aki_event_loop_init(&c.loop); - - camu_client_init(&c.client, &c.loop, client_callback, &c); - if (!camu_client_login(&c.client, al_str_c("andrew"), CAMU_SERVER_IP, CAMU_PORT)) { - goto err; - } - - if (c.action == CMC_ACTION_CREATE) { - if (c.type == CMC_SEARCH) { - camu_client_create_search(&c.client, &module, &query); - } - } - - aki_event_loop_run(&c.loop); - - if (c.action == CMC_ACTION_OPEN && c.ui.nc) notcurses_stop(c.ui.nc); - - camu_client_close(&c.client); - - aki_common_close(); - - return EXIT_SUCCESS; -err: - return EXIT_FAILURE; -} diff --git a/src/fruits/cmc/cmc.h b/src/fruits/cmc/cmc.h deleted file mode 100644 index 625d256..0000000 --- a/src/fruits/cmc/cmc.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include - -#include "../../libclient/client.h" - -#include "tui/ui.h" - -enum { - CMC_LIST = 0, - CMC_SEARCH -}; - -enum { - CMC_ACTION_OPEN = 0, - CMC_ACTION_LIST, - CMC_ACTION_CREATE, -}; - -struct cmc { - u8 type; - u8 action; - struct aki_event_loop loop; - struct camu_client client; - struct cmc_ui ui; -}; diff --git a/src/fruits/cmc/cmc.py b/src/fruits/cmc/cmc.py new file mode 100644 index 0000000..5c0a5b6 --- /dev/null +++ b/src/fruits/cmc/cmc.py @@ -0,0 +1,17 @@ +import cffi +import locale +from notcurses import notcurses + +ffi = cffi.FFI() + +print(ffi.string(notcurses.lib.notcurses_version())) + +class CMC: + def __init__(self): + pass + +c = CMC() + +locale.setlocale(locale.LC_ALL, "") +nc = notcurses.Notcurses() +nc.render() diff --git a/src/fruits/cmc/meson.build b/src/fruits/cmc/meson.build deleted file mode 100644 index 57ded9c..0000000 --- a/src/fruits/cmc/meson.build +++ /dev/null @@ -1,15 +0,0 @@ -cmc_src = [ - 'cmc.c', - 'tui/ui.c', - 'tui/resources.c', - 'tui/pager.c', - 'tui/view_twitter.c', - 'tui/pane_lists.c', - 'tui/pane_search.c' -] -cmc_deps = [common_deps, libclient, subproject('stb').get_variable('stb')] - -notcurses = dependency('notcurses') -cmc_deps += [notcurses] - -executable('cmc', cmc_src, dependencies: cmc_deps) diff --git a/src/fruits/cmc/tui/pager.c b/src/fruits/cmc/tui/pager.c deleted file mode 100644 index a672315..0000000 --- a/src/fruits/cmc/tui/pager.c +++ /dev/null @@ -1,88 +0,0 @@ -#include "pager.h" - -void cmc_pager_init(struct cmc_pager *pager, u32 page) -{ - pager->find_anchor = PAGE_NONE; - pager->anchor = 0; - pager->redraw = true; - pager->selected = 0; - al_array_init(pager->prev); - pager->max_page_num = page; - pager->min_page_num = page; - pager->end_of_page = -1; - pager->pending_requests = 0; - al_array_init(pager->pages); -} - -static bool any_pending_requests(struct cmc_view_page *page) -{ - struct cmc_view *view; - al_array_foreach(page->views, j, view) { - if (view->pending_requests > 0) { - return true; - } - } - return false; -} - -bool cmc_pager_maybe_process_pages(struct cmc_pager *pager, void (*func)(void *, struct cmc_view_page *), void *userdata) -{ - bool any_page_processed = false; - struct cmc_view_page *page; - al_array_foreach(pager->pages, i, page) { - if (page->processed) continue; - if (!any_pending_requests(page)) { - page->processed = true; - func(userdata, page); - any_page_processed = true; - } - } - return any_page_processed; -} - -struct cmc_view_page *cmc_get_page_from_num(struct cmc_pager *pager, u32 num) -{ - struct cmc_view_page *page; - al_array_foreach(pager->pages, i, page) { - if (page->num == num) return page; - } - return NULL; -} - -struct cmc_view_page *cmc_pager_page_containing_index(struct cmc_pager *pager, u32 index, u32 *offset) -{ - u32 count = 0, size = 0; - struct cmc_view_page *page = NULL; - for (u32 i = pager->min_page_num; i <= pager->max_page_num; i++) { - if (!(page = cmc_get_page_from_num(pager, i))) continue; - size = page->views.size; - count += size; - if (count > index) { - if (offset) *offset = size - (count - index); - return page; - } - } - return NULL; -} - -bool cmc_pager_iterate_to_height(struct cmc_pager *pager, u32 height, u32 anchor, u32 *count, - void (*func)(void *, struct cmc_view *, u32), void *userdata) -{ - u32 y = 0, offset; - struct cmc_view_page *page = cmc_pager_page_containing_index(pager, anchor, &offset); - if (!page) return false; - struct cmc_view *view; - do { - // If the page is not processed, return out of space as to not display a discontinuity. - if (!page->processed) return true; - for (; offset < page->views.size; offset++) { - view = al_array_at(page->views, offset); - // If this post would draw past the height, return out of space. - if (y + view->rows > height) return true; - if (func) func(userdata, view, y); - (*count)++; - y += view->rows + 1; - } - } while ((offset = 0, (page = cmc_get_page_from_num(pager, page->num + 1)))); - return false; -} diff --git a/src/fruits/cmc/tui/pager.h b/src/fruits/cmc/tui/pager.h deleted file mode 100644 index 6aeeb9c..0000000 --- a/src/fruits/cmc/tui/pager.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include - -#include "view.h" - -enum { - PAGE_NONE = 0, - PAGE_UP, - PAGE_DOWN -}; - -struct cmc_pager { - u8 find_anchor; - s32 anchor; - bool redraw; - u32 count; - s32 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; -}; - -void cmc_pager_init(struct cmc_pager *pager, u32 page); -bool cmc_pager_maybe_process_pages(struct cmc_pager *pager, void (*func)(void *, struct cmc_view_page *), void *userdata); -struct cmc_view_page *cmc_get_page_from_num(struct cmc_pager *pager, u32 num); -struct cmc_view_page *cmc_pager_page_containing_index(struct cmc_pager *pager, u32 index, u32 *offset); -bool cmc_pager_iterate_to_height(struct cmc_pager *pager, u32 height, u32 anchor, u32 *count, - void (*func)(void *, struct cmc_view *, u32), void *userdata); diff --git a/src/fruits/cmc/tui/pane_lists.c b/src/fruits/cmc/tui/pane_lists.c deleted file mode 100644 index 6367e57..0000000 --- a/src/fruits/cmc/tui/pane_lists.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "../cmc.h" - -#include "pane_lists.h" -#include "ui.h" - -void cmc_pane_lists_init(struct cmc_pane_lists *pane, struct cmc_ui *ui) -{ - pane->offset = 0; - pane->selected = 0; - pane->list = al_array_at(ui->c->client.state.lists, 0); - pane->ui = ui; -} - -bool cmc_pane_lists_handle_input(struct cmc_pane_lists *pane, struct ncinput *input) -{ - if (!(input->evtype == NCTYPE_PRESS || input->evtype == NCTYPE_UNKNOWN)) return false; - s32 rows = pane->ui->rows - 1; - s32 size = (s32)pane->list->entries.size; - if (input->id == 'j') { - if (pane->selected + 1 < size) { - pane->selected++; - if (pane->selected - pane->offset >= rows) { - pane->offset += rows; - } - return true; - } - } else if (input->id == 'k') { - if (pane->selected - 1 >= 0) { - pane->selected--; - if (pane->selected - pane->offset < 0) { - pane->offset -= rows; - } - return true; - } - } else if (input->id == 'n') { - if (pane->offset + rows < size) { - pane->offset += rows; - pane->selected += rows; - if (pane->selected >= size) { - pane->selected = size - 1; - } - return true; - } - } else if (input->id == 'p') { - if (pane->offset - rows >= 0) { - pane->offset -= rows; - pane->selected -= rows; - return true; - } - } else if (input->id == NCKEY_RETURN) { - camu_client_skipto(&pane->ui->c->client, pane->selected); - } - return false; -} - -bool cmc_pane_lists_handle_input_overlay(struct cmc_pane_lists *pane, struct ncinput *input) -{ - (void)pane; - (void)input; - return false; -} - -void cmc_pane_lists_draw_overlay(struct cmc_pane_lists *pane) -{ - (void)pane; -} - -void cmc_pane_lists_refresh(struct cmc_pane_lists *pane) -{ - struct cmc_ui *ui = pane->ui; - - ncplane_cursor_move_yx(ui->p, 0, 0); - ncplane_erase(ui->p); - notcurses_refresh(ui->nc, NULL, NULL); - notcurses_render(ui->nc); - - str *unique_id; - u32 rows = AL_MIN(pane->list->entries.size - pane->offset, ui->rows - 1); - for (u32 i = pane->offset; i < pane->offset + rows; i++) { - unique_id = &al_array_at(pane->list->entries, i); - if ((s32)i == pane->selected) { - ncplane_set_fg_palindex(ui->p, 4); - } else { - ncplane_set_fg_default(ui->p); - } - u32 cols = AL_MIN(unique_id->len, ui->cols); - for (u32 j = 0; j < cols; j++) { - ncplane_putchar_yx(ui->p, i - pane->offset, j + 1, al_str_at(unique_id, j)); - } - } - - notcurses_render(ui->nc); -} diff --git a/src/fruits/cmc/tui/pane_lists.h b/src/fruits/cmc/tui/pane_lists.h deleted file mode 100644 index 323eb52..0000000 --- a/src/fruits/cmc/tui/pane_lists.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include - -#include "../../libclient/client.h" - -struct cmc_ui; -struct cmc_pane_lists { - s32 offset; - s32 selected; - struct camu_list *list; - struct cmc_ui *ui; -}; - -void cmc_pane_lists_init(struct cmc_pane_lists *pane, struct cmc_ui *ui); -bool cmc_pane_lists_handle_input(struct cmc_pane_lists *pane, struct ncinput *input); -bool cmc_pane_lists_handle_input_overlay(struct cmc_pane_lists *pane, struct ncinput *input); -void cmc_pane_lists_draw_overlay(struct cmc_pane_lists *pane); -void cmc_pane_lists_refresh(struct cmc_pane_lists *pane); diff --git a/src/fruits/cmc/tui/pane_search.c b/src/fruits/cmc/tui/pane_search.c deleted file mode 100644 index 002a4d1..0000000 --- a/src/fruits/cmc/tui/pane_search.c +++ /dev/null @@ -1,335 +0,0 @@ -#include -#include - -#include "../cmc.h" - -#include "pane_search.h" -#include "view_twitter.h" -#include "ui.h" - -static void request_page_internal(struct cmc *c, struct cmc_search *search, u32 num) -{ - search->pager.pending_requests++; - camu_client_get_page(&c->client, search->search, num); -} - -void cmc_pane_search_init(struct cmc_pane_search *pane, struct cmc_ui *ui) -{ - pane->ui = ui; - al_array_init(pane->searches); - pane->search = NULL; - struct camu_search *s; - al_array_foreach(pane->ui->c->client.state.searches, i, s) { - struct cmc_search *search = al_alloc_object(struct cmc_search); - search->search = s; - cmc_pager_init(&search->pager, s->page); - al_array_push(pane->searches, search); - } - if (pane->searches.size > 0) { - pane->search = al_array_at(pane->searches, 0); - } -} - -static struct cmc_view *get_view_by_visual_index(struct cmc_pager *pager, s32 index) -{ - u32 offset; - struct cmc_view_page *page = cmc_pager_page_containing_index(pager, pager->anchor + index, &offset); - if (!page || !page->processed) return NULL; - return al_array_at(page->views, offset); -} - -// 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_pane_search *pane, struct cmc_pager *pager, u32 index, s32 offset) -{ - struct cmc_view_page *page = cmc_pager_page_containing_index(pager, index, NULL); - if (page) { - s32 req = (s32)page->num + offset; - if (req < 0) return false; - request_page_internal(pane->ui->c, pane->search, req); - } - return true; -} - -static void add_selected_from_index(struct cmc *c, struct cmc_pager *pager, u32 index) -{ - struct cmc_view *view = get_view_by_visual_index(pager, pager->selected); - if (view && view->post->media.size > index) { - camu_client_add(&c->client, &view->post->unique_id, index, true); - } -} - -#define ON_LAST_PAGE(pager) \ - (pager->end_of_page >= 0 && pager->anchor + pager->count > (u32)pager->end_of_page) - -bool cmc_pane_search_handle_input(struct cmc_pane_search *pane, struct ncinput *input) -{ - struct cmc_pager *pager = &pane->search->pager; - if (!(input->evtype == NCTYPE_PRESS || input->evtype == NCTYPE_UNKNOWN)) return false; - if (input->id == 'j') { - if (pager->selected + 1 >= (s32)pager->count) { - if (pager->pending_requests > 0 || pager->find_anchor != PAGE_NONE) return false; - if (ON_LAST_PAGE(pager)) return false; - if (pager->count == 0) pager->count = 1; - u32 index = pager->anchor + pager->count; - pager->find_anchor = PAGE_DOWN; - struct cmc_view_page *page = cmc_pager_page_containing_index(pager, index, NULL); - if (!page) { - request_page_at_page_offset(pane, pager, index - 1, 1); - return false; - } - } else { - pager->selected++; - } - return true; - } else if (input->id == 'k') { - if (pager->selected - 1 < 0) { - if (pager->pending_requests > 0 || pager->find_anchor != PAGE_NONE) return false; - if (pager->anchor - 1 < 0 && !request_page_at_page_offset(pane, pager, 0, -1)) { - // The reason to do this here instead of letting it resolve in - // refresh_pane_pager() is so we can return early if - // we are at the top of the page. - return false; - } - pager->anchor--; - pager->find_anchor = PAGE_UP; - // Wait for the requested previous page to come in. - if (pager->anchor < 0) return false; - } else { - pager->selected--; - } - return true; - } else if (input->id == NCKEY_RETURN || input->id == '1') { - add_selected_from_index(pane->ui->c, pager, 0); - } else if (input->id == '2') { - add_selected_from_index(pane->ui->c, pager, 1); - } else if (input->id == '3') { - add_selected_from_index(pane->ui->c, pager, 2); - } else if (input->id == '4') { - add_selected_from_index(pane->ui->c, pager, 3); - } - return false; -} - -bool cmc_pane_search_handle_input_overlay(struct cmc_pane_search *pane, struct ncinput *input) -{ - if (!(input->evtype == NCTYPE_PRESS || input->evtype == NCTYPE_UNKNOWN)) return false; - struct cmc_ui *ui = pane->ui; - struct cmc_pager *pager = &pane->search->pager; - if (input->id == NCKEY_TAB) { - pager->redraw = true; - } else if (input->id == 'j') { - if (ui->overlay.selected + 1 < (s32)pane->searches.size) { - ui->overlay.selected++; - return true; - } - } else if (input->id == 'k') { - if (ui->overlay.selected - 1 >= 0) { - ui->overlay.selected--; - return true; - } - } else if (input->id == NCKEY_RETURN) { - cmc_ui_toggle_overlay(ui); - struct cmc_view *view; - al_array_foreach(pager->prev, j, view) { - ncplane_erase(view->container); - ncplane_reparent_family(view->container, view->container); - } - pager->prev.size = 0; - pane->search = al_array_at(pane->searches, ui->overlay.selected); - pager = &pane->search->pager; - pager->redraw = true; - return true; - } - return false; -} - -void cmc_pane_search_maybe_draw_overlay(struct cmc_pane_search *pane) -{ - struct cmc_ui *ui = pane->ui; - - if (!ui->overlay.active) return; - - ncplane_cursor_move_yx(ui->overlay.p, 0, 0); - ncplane_erase(ui->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); - - ncchannel_set_rgb8(&background, 124, 12, 53); - ncchannels_set_bchannel(&color, background); - ncchannels_set_fchannel(&color, background); - ncplane_gradient(ui->overlay.p, 0, 0, ui->overlay.rows, ui->overlay.cols, "", 0, color, color, color, color); - - ncplane_set_bg_rgb8(ui->overlay.p, 124, 12, 53); - - struct cmc_search *search; - al_array_foreach(ui->search.searches, i, search) { - if ((s32)i == ui->overlay.selected) { - ncplane_set_fg_palindex(ui->overlay.p, 4); - } else { - ncplane_set_fg_default(ui->overlay.p); - } - str *query = &search->search->query; - for (u32 j = 0; j < query->len; j++) { - ncplane_putchar_yx(ui->overlay.p, i, j + 1, al_str_at(query, j)); - } - } -} - -static void draw_selection_line(struct cmc_view *view, u32 y, u32 rows) -{ - ncplane_move_yx(view->container, y, 0); - wchar_t box = L'█'; - u32 k = ((struct cmc_view_twitter *)view)->repost ? 1 : 0; - for (; k < rows - 1; k++) { - ncplane_cursor_move_yx(view->container, k, 0); - ncplane_putwc(view->container, box); - } -} - -void cmc_pane_search_refresh(struct cmc_pane_search *pane) -{ - struct cmc_pager *pager = &pane->search->pager; - if (pager->pending_requests > 0) return; - - u32 page = pane->search->search->page; - if (!cmc_get_page_from_num(pager, page)) { - request_page_internal(pane->ui->c, pane->search, page); - return; - } - - if (pager->find_anchor == PAGE_UP) { - u32 count; - // If find_anchor = PAGE_UP, search->anchor is index of the requested view. - s32 anchor = pager->anchor; - while (anchor >= 0) { - count = 0; - cmc_pager_iterate_to_height(pager, pane->ui->rows, anchor, &count, NULL, NULL); - // TODO: Handle count == 0. - // Is the requested view still visible? - if ((s32)(anchor + count) - 1 < pager->anchor) break; - anchor--; - // Load more posts if needed. - if (anchor < 0 && request_page_at_page_offset(pane, pager, 0, -1)) return; - } - anchor++; // We want the anchor before the last attempt above. - pager->selected = abs(pager->anchor - anchor); - pager->anchor = anchor; - pager->redraw = true; - pager->find_anchor = PAGE_NONE; - } else if (pager->find_anchor == PAGE_DOWN) { - // When moving down we already know how many views are being displayed - // so just move the anchor 1 past that. - pager->selected = 0; - pager->anchor += pager->count; - pager->redraw = true; - pager->find_anchor = PAGE_NONE; - } - - cmc_pane_search_maybe_draw_overlay(pane); - - struct cmc_view *view; - bool out_of_space = true; - if (pager->redraw) { - al_array_foreach(pager->prev, j, view) { - ncplane_erase(view->container); - ncplane_reparent_family(view->container, view->container); - } - pager->prev.size = 0; - pager->count = 0; - out_of_space = cmc_pager_iterate_to_height(pager, pane->ui->rows, pager->anchor, &pager->count, cmc_draw_tweet_func, pane->ui); - } else { - al_array_foreach(pager->prev, j, view) { - ncplane_erase(view->container); - } - } - if ((view = get_view_by_visual_index(pager, pager->selected))) { - draw_selection_line(view, view->y, view->rows); - } - if (pager->redraw) { - pager->redraw = false; - notcurses_refresh(pane->ui->nc, NULL, NULL); - } - notcurses_render(pane->ui->nc); - if (pager->count > 0 && !out_of_space && pager->end_of_page == -1) { - request_page_at_page_offset(pane, pager, (pager->anchor + pager->count) - 1, 1); - } -} - -static struct cmc_search *get_search_from_id(struct cmc_pane_search *pane, s32 id) -{ - struct cmc_search *search; - al_array_foreach(pane->searches, i, search) { - if (search->search->id == id) return search; - } - return NULL; -} - -static void process_page_func(void *userdata, struct cmc_view_page *page) -{ - struct cmc_ui *ui = (struct cmc_ui *)userdata; - struct cmc_view *view; - al_array_foreach(page->views, i, view) { - view->rows = cmc_layout_tweet(ui, (struct cmc_view_twitter *)view, ui->cols); - cmc_draw_tweet((struct cmc_view_twitter *)view, view->container, view->rows, ui->cols); - } -} - -void cmc_ui_resources_callback(void *userdata) -{ - struct cmc_pane_search *pane = (struct cmc_pane_search *)userdata; - struct cmc_pager *pager = &pane->search->pager; - if (cmc_pager_maybe_process_pages(pager, process_page_func, pane->ui)) { - pager->redraw = true; - cmc_pane_search_refresh(pane); - } -} - -void cmc_ui_handle_results(struct cmc_pane_search *pane, struct camu_result_list *results) -{ - struct cmc_search *search = get_search_from_id(pane, results->search->id); - if (!search) return; - struct cmc_pager *pager = &search->pager; - pager->pending_requests--; - - struct cmc_view_page *page = al_alloc_object(struct cmc_view_page); - al_array_push(pager->pages, page); - page->num = results->num; - page->processed = false; - if (page->num > pager->max_page_num) pager->max_page_num = page->num; - if (page->num < pager->min_page_num) pager->min_page_num = page->num; - if (pager->find_anchor == PAGE_UP) { - pager->anchor += results->unique_ids.size; - if (pager->end_of_page >= 0) { - pager->end_of_page += results->unique_ids.size; - } - } - - str *unique_id; - struct cmc_view_twitter *view; - al_array_foreach_ptr(results->unique_ids, i, unique_id) { - view = cmc_create_view_twitter(pane->ui, unique_id); - al_array_push(page->views, &view->v); - } - - if (cmc_pager_maybe_process_pages(pager, process_page_func, pane->ui)) { - pager->redraw = true; - cmc_pane_search_refresh(pane); - } -} - -void cmc_ui_handle_end_of_results(struct cmc_pane_search *pane, s32 id) -{ - struct cmc_search *search = get_search_from_id(pane, id); - if (!search) return; - struct cmc_pager *pager = &search->pager; - pager->pending_requests--; - pager->end_of_page = (pager->anchor + pager->count) - 1; - if (cmc_pager_maybe_process_pages(pager, process_page_func, pane->ui)) { - pager->redraw = true; - cmc_pane_search_refresh(pane); - } -} diff --git a/src/fruits/cmc/tui/pane_search.h b/src/fruits/cmc/tui/pane_search.h deleted file mode 100644 index b7c5e2c..0000000 --- a/src/fruits/cmc/tui/pane_search.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include "../../libclient/client.h" - -#include "pager.h" - -struct cmc_search { - struct camu_search *search; - struct cmc_pager pager; -}; - -struct cmc_ui; -struct cmc_pane_search { - array(struct cmc_search *) searches; - struct cmc_search *search; - struct cmc_ui *ui; -}; - -void cmc_pane_search_init(struct cmc_pane_search *pane, struct cmc_ui *ui); -bool cmc_pane_search_handle_input(struct cmc_pane_search *pane, struct ncinput *input); -bool cmc_pane_search_handle_input_overlay(struct cmc_pane_search *pane, struct ncinput *input); -void cmc_pane_search_draw_overlay(struct cmc_pane_search *pane); -void cmc_pane_search_refresh(struct cmc_pane_search *pane); - -void cmc_ui_resources_callback(void *userdata); -void cmc_ui_handle_results(struct cmc_pane_search *pane, struct camu_result_list *results); -void cmc_ui_handle_end_of_results(struct cmc_pane_search *pane, s32 id); diff --git a/src/fruits/cmc/tui/resources.c b/src/fruits/cmc/tui/resources.c deleted file mode 100644 index 19513bf..0000000 --- a/src/fruits/cmc/tui/resources.c +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#define STB_IMAGE_IMPLEMENTATION -#define STB_IMAGE_STATIC -#define STBI_NO_STDIO -#define STBI_NO_FAILURE_STRINGS -#define STBI_MALLOC al_malloc -#define STBI_FREE al_free -#define STBI_REALLOC al_realloc -#include - -#include "resources.h" -#include "view.h" - -static void resource_client_callback(void *userdata, u16 id, struct aki_buffer *buffer) -{ - struct cmc_resources *resources = (struct cmc_resources *)userdata; - struct cmc_view_resource *resource; - al_array_foreach(resources->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, 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(resources->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; - } - } - resources->callback(resources->userdata); -} - -bool cmc_resources_connect(struct cmc_resources *resources, struct notcurses *nc, - struct aki_event_loop *loop, str *addr, s32 port) -{ - resources->nc = nc; - resources->pending_requests = 0; - camu_resource_client_init(&resources->client, loop, resource_client_callback, resources); - return camu_resource_client_connect(&resources->client, addr, port); -} - -void cmc_resources_set_callback(struct cmc_resources *resources, void (*callback)(void *), void *userdata) -{ - resources->callback = callback; - resources->userdata = userdata; -} - -static struct cmc_view_resource *create_resource(struct cmc_resources *resources, str *unique_id, u32 index) -{ - struct cmc_view_resource *resource = al_alloc_object(struct cmc_view_resource); - al_array_push(resources->cache, resource); - resource->vopts.blitter = NCBLIT_PIXEL; - resource->vopts.scaling = NCSCALE_SCALE; - resource->id = al_rand_u16(); - al_str_clone(&resource->unique_id, unique_id); - resource->index = index; - al_array_init(resource->refs); - camu_resource_request(&resources->client, unique_id, index, resource->id); - return resource; -} - -struct cmc_view_resource *cmc_get_resource_from_cache(struct cmc_resources *resources, struct cmc_view *view, - str *unique_id, u32 index) -{ - struct cmc_view_resource *resource; - al_array_foreach(resources->cache, i, resource) { - if (al_str_eq(&resource->unique_id, unique_id) && resource->index == index) { - return resource; - } - } - resource = create_resource(resources, unique_id, index); - view->pending_requests++; - al_array_push(resource->refs, view); - return resource; -} diff --git a/src/fruits/cmc/tui/resources.h b/src/fruits/cmc/tui/resources.h deleted file mode 100644 index f12d861..0000000 --- a/src/fruits/cmc/tui/resources.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include - -#include "../../libclient/resource_client.h" - -struct cmc_view; -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; -}; - -struct cmc_resources { - struct camu_resource_client client; - u32 pending_requests; - struct notcurses *nc; - array(struct cmc_view_resource *) cache; - void (*callback)(void *); - void *userdata; -}; - -bool cmc_resources_connect(struct cmc_resources *resources, struct notcurses *nc, - struct aki_event_loop *loop, str *addr, s32 port); -void cmc_resources_set_callback(struct cmc_resources *resources, void (*callback)(void *), void *userdata); -struct cmc_view_resource *cmc_get_resource_from_cache(struct cmc_resources *resources, struct cmc_view *view, - str *unique_id, u32 index); diff --git a/src/fruits/cmc/tui/ui.c b/src/fruits/cmc/tui/ui.c deleted file mode 100644 index 2c406bd..0000000 --- a/src/fruits/cmc/tui/ui.c +++ /dev/null @@ -1,144 +0,0 @@ -#include "../../server/common.h" - -#include "../cmc.h" - -#include "ui.h" - -/* -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); - -void cmc_ui_toggle_overlay(struct cmc_ui *ui) -{ - if (ui->overlay.active) { - ncplane_reparent_family(ui->overlay.p, ui->overlay.p); - } else { - ncplane_reparent_family(ui->overlay.p, ui->p); - } - ui->overlay.active = !ui->overlay.active; -} - -static void refresh_internal(struct cmc_ui *ui) -{ - switch (ui->pane) { - case CMC_PANE_LIBRARY: - break; - case CMC_PANE_LISTS: - cmc_pane_lists_refresh(&ui->lists); - break; - case CMC_PANE_BROWSE: - break; - case CMC_PANE_SEARCH: - cmc_pane_search_refresh(&ui->search); - break; - } -} - -static void input_poll_callback(void *userdata, s32 revents) -{ - struct cmc_ui *ui = (struct cmc_ui *)userdata; - (void)revents; - struct ncinput input; - u32 ret; - bool do_refresh = false; - do { - ret = notcurses_get_nblock(ui->nc, &input); - if (ret == (u32)-1 || ret == 0) break; - if (input.evtype == NCTYPE_PRESS || input.evtype == NCTYPE_UNKNOWN) { - if (input.id == 'q') { - aki_event_loop_break(&ui->c->loop); - break; - } else if (input.id == NCKEY_TAB) { - cmc_ui_toggle_overlay(ui); - do_refresh = true; - } - switch (ui->pane) { - case CMC_PANE_LIBRARY: - break; - case CMC_PANE_LISTS: - if (ui->overlay.active) { - do_refresh |= cmc_pane_lists_handle_input_overlay(&ui->lists, &input); - } else { - do_refresh |= cmc_pane_lists_handle_input(&ui->lists, &input); - } - break; - case CMC_PANE_BROWSE: - break; - case CMC_PANE_SEARCH: - if (ui->overlay.active) { - do_refresh |= cmc_pane_search_handle_input_overlay(&ui->search, &input); - } else { - do_refresh |= cmc_pane_search_handle_input(&ui->search, &input); - } - break; - } - } - } while (1); - if (do_refresh) refresh_internal(ui); -} - -static s32 resize_cb(struct ncplane *p) -{ - struct cmc_ui *ui = (struct cmc_ui *)ncplane_userptr(p); - notcurses_stddim_yx(ui->nc, &ui->rows, &ui->cols); - ui->cols -= 4; - ui->rows -= 1; - struct ncplane_options nopts = { 0 }; - nopts.cols = ui->cols; - nopts.rows = ui->rows; - nopts.x = 3; - nopts.y = 1; - nopts.flags = 0; - ui->p = ncplane_create(p, &nopts); - ui->pane = CMC_PANE_SEARCH; - ui->overlay.cols = nopts.cols = 35; - ui->overlay.rows = nopts.rows = 20; - nopts.x = (ui->cols / 2) - (ui->overlay.cols / 2); - nopts.y = (ui->rows / 2) - (ui->overlay.rows / 2); - ui->overlay.p = ncpile_create(ui->nc, &nopts); - return 0; -} - -bool cmc_ui_open(struct cmc_ui *ui, struct cmc *c) -{ - if (!(ui->nc = notcurses_init(NULL, stdin))) return false; - - ui->c = c; - - cmc_pane_lists_init(&ui->lists, ui); - cmc_pane_search_init(&ui->search, ui); - - //if (!cmc_resources_connect(&ui->resources, ui->nc, &ui->c->loop, CAMU_SERVER_IP, CAMU_RESOURCE_PORT)) { - // return false; - //} - cmc_resources_set_callback(&ui->resources, cmc_ui_resources_callback, &ui->search); - - aki_poll_init(&ui->input, input_poll_callback, ui); - aki_poll_set(&ui->input, notcurses_inputready_fd(ui->nc), AKI_POLL_READ); - aki_poll_start(&ui->input, &c->loop); - - ui->overlay.active = false; - ui->overlay.selected = 0; - - struct ncplane *stdplane = notcurses_stdplane(ui->nc); - ncplane_set_resizecb(stdplane, resize_cb); - ncplane_set_userptr(stdplane, ui); - resize_cb(stdplane); - - refresh_internal(ui); - - return true; -} diff --git a/src/fruits/cmc/tui/ui.h b/src/fruits/cmc/tui/ui.h deleted file mode 100644 index 76ea353..0000000 --- a/src/fruits/cmc/tui/ui.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include - -#include "resources.h" -#include "pane_lists.h" -#include "pane_search.h" - -enum { - CMC_PANE_LIBRARY = 0, - CMC_PANE_LISTS, - CMC_PANE_BROWSE, - CMC_PANE_SEARCH -}; - -struct cmc; -struct cmc_ui { - struct notcurses *nc; - u32 cols; - u32 rows; - struct ncplane *p; - u8 pane; - struct { - struct ncplane *p; - u32 cols; - u32 rows; - bool active; - s32 selected; - } overlay; - struct aki_poll input; - struct cmc_resources resources; - struct cmc_pane_lists lists; - struct cmc_pane_search search; - struct cmc *c; -}; - -bool cmc_ui_open(struct cmc_ui *ui, struct cmc *c); -void cmc_ui_toggle_overlay(struct cmc_ui *ui); -void cmc_ui_maybe_draw_overlay(struct cmc_ui *ui); diff --git a/src/fruits/cmc/tui/view.h b/src/fruits/cmc/tui/view.h deleted file mode 100644 index 3c521c3..0000000 --- a/src/fruits/cmc/tui/view.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include - -enum { - CMC_VIEW_NONE = 0, - CMC_VIEW_SINGLE, - CMC_VIEW_PARTITION -}; - -struct cmc_view { - struct sho_post *post; - struct ncplane *container; - u32 pending_requests; - u32 rows; - u32 y; -}; - -struct cmc_view_page { - u32 num; - bool processed; - array(struct cmc_view *) views; -}; diff --git a/src/fruits/cmc/tui/view_twitter.c b/src/fruits/cmc/tui/view_twitter.c deleted file mode 100644 index b1070a4..0000000 --- a/src/fruits/cmc/tui/view_twitter.c +++ /dev/null @@ -1,370 +0,0 @@ -#include "../cmc.h" - -#include "view_twitter.h" - -#define TEXT_OFFSET 10 - -#define PROFILE_SIZE 5 -#define PREVIEW_OFFSET_WIDTH 26 - -static u32 count_wrapped_lines(wstr *w, u32 width) -{ - u32 counter = 0, wraps = 1; - for (u32 i = 0; i < w->len; i++) { - wchar_t wc = al_wstr_at(w, i); - s32 wcwidth = al_wchar_width(wc); - bool newline = wc == L'\n'; - if (newline || counter + wcwidth > width) { - wraps++; - counter = 0; - } - if (newline) continue; - counter += wcwidth; - } - return wraps; -} - -u32 cmc_layout_tweet(struct cmc_ui *ui, struct cmc_view_twitter *view, u32 width) -{ - struct sho_post *post = view->v.post; - struct sho_post *repost = view->repost; - - u32 rows = 1; - - view->text.width = width - TEXT_OFFSET; - view->text.height = 1 + count_wrapped_lines(&post->text, view->text.width); - view->text.y = repost ? 1 : 0; - view->text.height += view->text.y; - rows += view->text.height; - - if (view->preview.mode == CMC_VIEW_NONE) { - return rows; - } - - view->preview.width = view->text.width - 1; - view->preview.x = TEXT_OFFSET; - u32 partition_width = view->preview.width / 2; - - struct ncplane_options opts = { 0 }; - opts.cols = view->preview.width; - opts.rows = ncplane_dim_y(notcurses_stdplane(ui->nc)) / 3; - opts.x = 0; - opts.y = 0; - struct ncplane *inter = ncplane_create(notcurses_stdplane(ui->nc), &opts); - - u32 left = 0, right = 0; - struct cmc_view_resource *resource; - switch (view->preview.mode) { - case CMC_VIEW_SINGLE: { - resource = view->preview.resources[0]; - resource->vopts.n = inter; - ncvisual_geom(ui->nc, resource->v, &resource->vopts, &resource->geom); - // rcellx == width case very untested. - left += resource->geom.rcelly + (s32)((resource->geom.rcellx != view->preview.width) ? 1 : -1); - break; - } - case CMC_VIEW_PARTITION: { - ncplane_resize_simple(inter, opts.rows, partition_width); - - resource = view->preview.resources[0]; - resource->vopts.n = inter; - ncvisual_geom(ui->nc, resource->v, &resource->vopts, &resource->geom); - left += resource->geom.rcelly + 1; - - resource = view->preview.resources[1]; - resource->vopts.n = inter; - ncvisual_geom(ui->nc, resource->v, &resource->vopts, &resource->geom); - right += resource->geom.rcelly + 1; - - if (view->preview.resources[2]) { - resource = view->preview.resources[2]; - resource->vopts.n = inter; - ncvisual_geom(ui->nc, resource->v, &resource->vopts, &resource->geom); - left += resource->geom.rcelly + 1; - } - - if (view->preview.resources[3]) { - resource = view->preview.resources[3]; - resource->vopts.n = inter; - ncvisual_geom(ui->nc, resource->v, &resource->vopts, &resource->geom); - right += resource->geom.rcelly + 1; - } - - break; - } - } - - ncplane_destroy(inter); - - view->preview.height = AL_MAX(right, left); - - return rows + view->preview.height; -} - -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 >= 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); - } -} - -static void draw_tweet_resources(struct cmc_ui *ui, struct cmc_view_twitter *view) -{ - u32 partition_width = view->preview.width / 2; - struct cmc_view_resource *resource; - switch (view->preview.mode) { - case CMC_VIEW_NONE: - break; - case CMC_VIEW_SINGLE: { - resource = view->preview.resources[0]; - ncplane_resize_simple(view->preview.p[0], resource->geom.rcelly + 1, view->preview.width); - ncplane_move_yx(view->preview.p[0], view->text.height, view->preview.x); - resource->vopts.n = view->preview.p[0]; - ncvisual_blit(ui->nc, resource->v, &resource->vopts); - break; - } - case CMC_VIEW_PARTITION: { - resource = view->preview.resources[0]; - resource->vopts.n = view->preview.p[0]; - ncplane_resize_simple(view->preview.p[0], resource->geom.rcelly + 1, partition_width); - ncplane_move_yx(view->preview.p[0], view->text.height, view->preview.x); - ncvisual_blit(ui->nc, resource->v, &resource->vopts); - - resource = view->preview.resources[1]; - ncplane_resize_simple(view->preview.p[1], resource->geom.rcelly + 1, partition_width); - ncplane_move_yx(view->preview.p[1], view->text.height, (view->preview.x + view->preview.resources[0]->geom.rcellx) + 3); - resource->vopts.n = view->preview.p[1]; - ncvisual_blit(ui->nc, resource->v, &resource->vopts); - - if (view->preview.resources[2]) { - resource = view->preview.resources[2]; - ncplane_resize_simple(view->preview.p[2], resource->geom.rcelly + 1, partition_width); - ncplane_move_yx(view->preview.p[2], view->preview.resources[0]->geom.rcelly + view->text.height + 2, view->preview.x); - resource->vopts.n = view->preview.p[2]; - ncvisual_blit(ui->nc, resource->v, &resource->vopts); - } - - 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[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(ui->nc, resource->v, &resource->vopts); - } - - break; - } - } - - 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(ui->nc, resource->v, &resource->vopts); - } -} - -void cmc_draw_tweet_func(void *userdata, struct cmc_view *view, u32 y) -{ - struct cmc_ui *ui = (struct cmc_ui *)userdata; - struct cmc_search *search = ui->search.search; - ncplane_reparent_family(view->container, ui->p); - ncplane_move_yx(view->container, y, 0); - draw_tweet_resources(ui, (struct cmc_view_twitter *)view); - view->y = y; - al_array_push(search->pager.prev, view); -} - -static char format_buf[32]; - -void cmc_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); - - s32 offset = TEXT_OFFSET; - - if (repost) { - const wchar_t *retweet = L"⮤⮧"; - ncplane_set_styles(view->text.p, NCSTYLE_ITALIC); - ncplane_putwstr_yx(view->text.p, 0, offset - 3, retweet); - ncplane_set_styles(view->text.p, NCSTYLE_NONE); - ncplane_putwstr_yx(view->text.p, 0, offset, &al_wstr_at(&repost->author.display_name, 0)); - } - - ncplane_set_styles(view->text.p, NCSTYLE_BOLD); - - ncplane_putwstr_yx(view->text.p, view->text.y, offset, &al_wstr_at(&post->author.display_name, 0)); - offset += al_wstr_width(&post->author.display_name) + 1; - - ncplane_set_styles(view->text.p, NCSTYLE_NONE); - ncplane_putwstr_yx(view->text.p, view->text.y, offset, &al_wstr_at(&post->author.username, 0)); - offset += al_wstr_width(&post->author.username) + 1; - - ncplane_putwstr_yx(view->text.p, view->text.y, offset, L"∙"); - offset += 2; - - date_to_relative_date(post, format_buf); - ncplane_putstr_yx(view->text.p, view->text.y, offset, format_buf); - - u32 text_y = view->text.y + 1, text_x = TEXT_OFFSET; - for (u32 i = 0; i < post->text.len; i++) { - wchar_t wc = al_wstr_at(&post->text, i); - s32 wcwidth = al_wchar_width(wc); - bool newline = wc == L'\n'; - if (newline || text_x + wcwidth > width) { - text_y++; - text_x = TEXT_OFFSET; - } - if (newline) continue; - ncplane_putwc_yx(view->text.p, text_y, text_x, wc); - text_x += wcwidth; - } - - ncplane_resize_simple(view->stats.p, 1, view->text.width); - ncplane_move_yx(view->stats.p, rows - 1, TEXT_OFFSET); - - u32 part = 10; - offset = 0; - - if (post->comments.set) { - const wchar_t *comment = L"🗩"; - ncplane_set_styles(view->stats.p, NCSTYLE_ITALIC); - ncplane_putwstr_yx(view->stats.p, 0, offset, comment); - ncplane_set_styles(view->stats.p, NCSTYLE_NONE); - number_to_shorthand(post->comments.i, format_buf); - ncplane_putstr_yx(view->stats.p, 0, offset + 3, format_buf); - } - offset += part; - - if (post->reposts.set) { - const wchar_t *retweet = L"⮤⮧"; - ncplane_set_styles(view->stats.p, NCSTYLE_ITALIC); - ncplane_putwstr_yx(view->stats.p, 0, offset, retweet); - ncplane_set_styles(view->stats.p, NCSTYLE_NONE); - number_to_shorthand(post->reposts.i, format_buf); - ncplane_putstr_yx(view->stats.p, 0, offset + 3, format_buf); - } - offset += part; - - if (post->likes.set) { - //const wchar_t *like = L"❤"; - const wchar_t *like = L"♥"; - ncplane_set_styles(view->stats.p, NCSTYLE_ITALIC); - ncplane_putwstr_yx(view->stats.p, 0, offset, like); - ncplane_set_styles(view->stats.p, NCSTYLE_NONE); - number_to_shorthand(post->likes.i, format_buf); - ncplane_putstr_yx(view->stats.p, 0, offset + 2, format_buf); - } -} - -static void get_post_and_maybe_repost(struct cmc_ui *ui, str *unique_id, struct sho_post **post, struct sho_post **repost) -{ - struct sho_post_cache *cache = &ui->c->client.cache; - *post = sho_post_cache_get(cache, unique_id); - *repost = NULL; - if ((*post)->type == SHOKI_POST_REPOST) { - *repost = *post; - *post = sho_post_cache_get(cache, &(*post)->post.unique_id); - } -} - -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; - } -} - -struct cmc_view_twitter *cmc_create_view_twitter(struct cmc_ui *ui, str *unique_id) -{ - struct cmc_view_twitter *view = al_alloc_object(struct cmc_view_twitter); - get_post_and_maybe_repost(ui, 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; - default_options.x = 0; - default_options.y = 0; - - view->v.container = ncpile_create(ui->nc, &default_options); - view->text.p = ncplane_create(view->v.container, &default_options); - view->profile.p = ncplane_create(view->v.container, &default_options); - for (u32 i = 0; i < 4; i++) { - view->preview.p[i] = ncplane_create(view->v.container, &default_options); - } - view->stats.p = ncplane_create(view->v.container, &default_options); - - struct sho_post *post = view->v.post; - view->profile.resource = cmc_get_resource_from_cache(&ui->resources, &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] = cmc_get_resource_from_cache(&ui->resources, &view->v, &post->unique_id, i); - } - - switch (post->media.size) { - case 0: - view->preview.mode = CMC_VIEW_NONE; - break; - case 1: - view->preview.mode = CMC_VIEW_SINGLE; - break; - default: - view->preview.mode = CMC_VIEW_PARTITION; - break; - } - - return view; -} diff --git a/src/fruits/cmc/tui/view_twitter.h b/src/fruits/cmc/tui/view_twitter.h deleted file mode 100644 index 9792826..0000000 --- a/src/fruits/cmc/tui/view_twitter.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "ui.h" -#include "view.h" - -struct cmc_view_twitter { - struct cmc_view v; - struct sho_post *repost; - struct { - u32 width; - u32 height; - u32 y; - struct ncplane *p; - } text; - struct { - struct ncplane *p; - struct cmc_view_resource *resource; - } profile; - struct { - u8 mode; - u32 width; - u32 height; - u32 x; - struct ncplane *p[4]; - struct cmc_view_resource *resources[4]; - } preview; - struct { - struct ncplane *p; - } stats; -}; - -struct cmc_view_twitter *cmc_create_view_twitter(struct cmc_ui *ui, str *unique_id); -void cmc_draw_tweet(struct cmc_view_twitter *view, struct ncplane *container, u32 rows, u32 width); -u32 cmc_layout_tweet(struct cmc_ui *ui, struct cmc_view_twitter *view, u32 width); -void cmc_draw_tweet_func(void *userdata, struct cmc_view *view, u32 y); diff --git a/src/fruits/cmsrv/cmsrv.c b/src/fruits/cmsrv/cmsrv.c new file mode 100644 index 0000000..08f67dd --- /dev/null +++ b/src/fruits/cmsrv/cmsrv.c @@ -0,0 +1,196 @@ +#define CAMU_LOCAL_SOCKET + +#include +#include +#ifdef CAMU_LOCAL_SOCKET +#include +#endif +#include + +#include "../../server/server.h" +#include "../../server/list.h" +#include "../../server/common.h" +#include "../../cache/handlers/cdio.h" +#include "../../codec/ffmpeg/common.h" +#ifdef CAMU_LOCAL_SOCKET +#include "../../server/local_compat.h" +#endif + +#include "ui.h" + +struct cmsrv { + struct aki_event_loop loop; + struct aki_signal quit_signal; + struct camu_server server; +#ifdef CAMU_LOCAL_SOCKET + struct { + struct aki_socket sock; + struct aki_line_processor cli; + struct camu_local_compat compat; + } local; +#endif + struct cmsrv_ui ui; + struct aki_poll input_poll; + struct aki_timer render_timer; +}; + +#ifdef CAMU_LOCAL_SOCKET +static u8 server_line_callback(void *userdata, str *line) +{ + struct cmsrv *s = (struct cmsrv *)userdata; + struct camu_list *list = al_array_at(s->server.lists, 0); + if (al_str_eq(line, al_str_c(";PAUSE"))) { + lia_list_toggle_pause(&list->impl, LIANA_SEQUENCE_ANY, -1.0); + } else if (al_str_eq(line, al_str_c(";NEXT"))) { + lia_list_skip(&list->impl, LIANA_SEQUENCE_ANY, 1); + } else if (al_str_eq(line, al_str_c(";PREV"))) { + lia_list_skip(&list->impl, LIANA_SEQUENCE_ANY, -1); + } else if (al_str_eq(line, al_str_c(";SHUFFLE"))) { + lia_list_shuffle(&list->impl); + } else if (al_str_eq(line, al_str_c(";SORT"))) { + lia_list_sort(&list->impl); + } else if (al_str_eq(line, al_str_c(";REVERSE"))) { + lia_list_reverse(&list->impl); + } else if (al_str_eq(line, al_str_c(";CLEAR"))) { + lia_list_clear(&list->impl); + } else { + struct aki_packet *packet = aki_packet_create(); + aki_packet_write_str(packet, line); + camu_server_local_add(&s->server, packet); + } + return AKI_LINE_PROCESSOR_CONTINUE; +} +#endif + +static void meta_callback(void *userdata, u8 op, struct lia_list_entry *entry) +{ + struct cmsrv *s = (struct cmsrv *)userdata; + (void)s; + switch (op) { + case LIANA_META_PLAYING: { + struct camu_server_resource *resource = (struct camu_server_resource *)entry->opaque; +#ifdef CAMU_HAVE_PORTAL + if (resource->post) { + al_log_info("server", "now playing: %.*ls.", AL_WSTR_PRINTF(&resource->post->title)); + } else { +#endif + al_log_info("server", "now playing: %.*s.", AL_STR_PRINTF(&resource->unique_id)); +#ifdef CAMU_HAVE_PORTAL + } +#endif + break; + } + } +} + +static void render_timer_callback(void *userdata, struct aki_timer *timer) +{ + struct cmsrv *s = (struct cmsrv *)userdata; + (void)timer; + cmsrv_ui_render(&s->ui); + aki_timer_again(&s->render_timer); +} + +static void input_poll_callback(void *userdata, s32 revents) +{ + struct cmsrv *s = (struct cmsrv *)userdata; + (void)revents; + struct ncinput input; + do { + if (!cmsrv_ui_read_input(&s->ui, &input)) break; + if (input.evtype == NCTYPE_PRESS || input.evtype == NCTYPE_UNKNOWN) { + switch (input.id) { + case 'q': + aki_event_loop_break_one(&s->loop); + break; + case '1': + cmsrv_ui_set_pane(&s->ui, CMSRV_UI_LISTS); + break; + case '2': + cmsrv_ui_set_pane(&s->ui, CMSRV_UI_RESOURCES); + break; + case '3': + cmsrv_ui_set_pane(&s->ui, CMSRV_UI_NODES); + break; + } + } + } while (1); +} + +static void quit_signal_callback(void *userdata) +{ + struct cmsrv *s = (struct cmsrv *)userdata; + aki_event_loop_break_one(&s->loop); +} + +static s32 log_callback(void *userdata, char *message) +{ + struct cmsrv *s = (struct cmsrv *)userdata; + cmsrv_ui_push_message(&s->ui, message); + return al_strlen(message); +} + +static struct cmsrv s = { 0 }; + +static void sigint_handler(s32 signum) +{ + (void)signum; + aki_signal_send(&s.quit_signal); +} + +#ifndef _WIN32 +s32 main(s32 argc, char *argv[]) +#else +s32 wmain(s32 argc, wchar_t **argv) +#endif +{ + (void)argc; + (void)argv; + if (!aki_common_init() || !cmsrv_ui_init(&s.ui, &s.server)) return EXIT_FAILURE; + + signal(SIGINT, sigint_handler); + + al_set_print(log_callback, &s); + + camu_ff_set_default_log_callback(); + + aki_event_loop_init(&s.loop); + + aki_signal_init(&s.quit_signal, quit_signal_callback, &s); + aki_signal_start(&s.quit_signal, &s.loop); + + if (!camu_server_init(&s.server, CAMU_LOCAL_TYPE, &s.loop)) return EXIT_FAILURE; + camu_server_listen(&s.server, CAMU_LOCAL_ADDR, CAMU_PORT); + struct camu_list *list = al_array_at(s.server.lists, 0); + list->impl.callback = meta_callback; + list->impl.userdata = &s; + +#ifdef CAMU_LOCAL_SOCKET + s.local.sock.type = AKI_SOCKET_UNIX; + aki_socket_init(&s.local.sock); + aki_socket_set_blocking(&s.local.sock, false); + s.local.cli.callback = server_line_callback; + s.local.cli.userdata = &s; + aki_line_processor_init(&s.local.cli, al_str_c("\n")); + aki_line_processor_open_socket(&s.local.cli, &s.local.sock); + if (aki_socket_bind(&s.local.sock, CAMU_UNIX_LOCAL, 0) && aki_socket_listen(&s.local.sock)) { + aki_line_processor_run(&s.local.cli, &s.loop); + } +#endif + + aki_poll_init(&s.input_poll, input_poll_callback, &s); + aki_poll_set(&s.input_poll, cmsrv_ui_get_input_fd(&s.ui), AKI_POLL_READ); + aki_poll_start(&s.input_poll, &s.loop); + + aki_timer_init(&s.render_timer, &s.loop, render_timer_callback, &s); + aki_timer_set_repeat(&s.render_timer, AKI_TS_FROM_USEC(100000)); + aki_timer_again(&s.render_timer); + + aki_event_loop_run(&s.loop); + + cmsrv_ui_close(&s.ui); + + aki_common_close(); + + return EXIT_SUCCESS; +} diff --git a/src/fruits/cmsrv/meson.build b/src/fruits/cmsrv/meson.build new file mode 100644 index 0000000..ee8eb91 --- /dev/null +++ b/src/fruits/cmsrv/meson.build @@ -0,0 +1,19 @@ +cmsrv_src = ['cmsrv.c'] +cmsrv_deps = [server] +cmsrv_args = [] + +if get_option('portal').enabled() + cmsrv_deps += [portal] +endif + +use_tui = true +if use_tui + cmsrv_src += ['ui.c'] + cmsrv_deps += [dependency('notcurses-core')] +endif + +if is_windows and meson.is_cross_build() + cmsrv_args += ['-static', '-static-libgcc', '-static-libstdc++', '-municode', '-mwindows'] +endif + +executable('cmsrv', cmsrv_src, dependencies: cmsrv_deps, link_args: cmsrv_args) diff --git a/src/fruits/cmsrv/ui.c b/src/fruits/cmsrv/ui.c new file mode 100644 index 0000000..48f50ca --- /dev/null +++ b/src/fruits/cmsrv/ui.c @@ -0,0 +1,210 @@ +#include + +#include "../../server/list.h" + +#include "ui.h" + +#define LOG_RATIO 1.3 + +static s32 resize_cb(struct ncplane *p) +{ + struct cmsrv_ui *ui = (struct cmsrv_ui *)ncplane_userptr(p); + ui->pending_layout = true; + return 0; +} + +bool cmsrv_ui_init(struct cmsrv_ui *ui, struct camu_server *server) +{ + if (!(ui->nc = notcurses_core_init(NULL, stdin))) { + return false; + } + ui->server = server; + notcurses_stddim_yx(ui->nc, &ui->term_rows, &ui->term_cols); + struct ncplane *stdplane = notcurses_stdplane(ui->nc); + ncplane_set_resizecb(stdplane, resize_cb); + ncplane_set_userptr(stdplane, ui); + ui->pending_layout = true; + al_array_init(ui->log.messages); + return true; +} + +s32 cmsrv_ui_get_input_fd(struct cmsrv_ui *ui) +{ + return notcurses_inputready_fd(ui->nc); +} + +bool cmsrv_ui_read_input(struct cmsrv_ui *ui, struct ncinput *input) +{ + al_memset(input, 0, sizeof(struct ncinput)); + u32 ret = notcurses_get_nblock(ui->nc, input); + return !(ret == (u32)-1 || ret == 0); +} + +void cmsrv_ui_set_pane(struct cmsrv_ui *ui, u8 pane) +{ + ui->pane = pane; +} + +void cmsrv_ui_push_message(struct cmsrv_ui *ui, char *message) +{ + al_array_push(ui->log.messages, message); +} + +static void layout_log(struct cmsrv_ui *ui, struct ncplane *parent) +{ + struct ncplane_options nopts = { + .rows = ui->term_rows / LOG_RATIO, + .cols = ui->term_cols, + .x = 0 + }; + nopts.y = ui->term_rows - nopts.rows; + ui->log.p = ncplane_create(parent, &nopts); +} + +static void erase_log(struct cmsrv_ui *ui) +{ + struct ncplane *p = ui->log.p; + ncplane_erase(p); +} + +static void render_log(struct cmsrv_ui *ui) +{ + struct ncplane *p = ui->log.p; + ncplane_erase(p); + + u32 max_width = ncplane_dim_x(p) - 2; + u32 max_height = ncplane_dim_y(p) - 2; + + u64 c = 0; + ncchannels_set_fg_default(&c); + ncplane_rounded_box(p, NCSTYLE_NONE, c, max_height + 1, max_width + 1, 0); + + u32 size = ui->log.messages.size; + u32 index = size > max_height ? size - max_height : 0; + for (u32 i = index; i < size; i++) { + ncplane_putnstr_yx(p, (i - index) + 1, 1, max_width, al_array_at(ui->log.messages, i)); + } +} + +static void layout_lists(struct cmsrv_ui *ui, struct ncplane *parent) +{ + struct ncplane_options nopts = { + .rows = ui->term_rows - (ui->term_rows / LOG_RATIO), + .cols = ui->term_cols, + .x = 0, + .y = 0 + }; + ui->lists.p = ncplane_create(parent, &nopts); +} + +static void erase_lists(struct cmsrv_ui *ui) +{ + struct ncplane *p = ui->lists.p; + ncplane_erase(p); +} + +static void render_lists(struct cmsrv_ui *ui) +{ + struct ncplane *p = ui->lists.p; + + u32 max_width = ncplane_dim_x(p); + u32 max_height = ncplane_dim_y(p); + u32 current_line = 0; + s32 entries_per_list = 10; + + struct camu_list *list; + al_array_foreach(ui->server->lists, i, list) { + if (current_line++ >= max_height) break; + char *c_str = al_str_to_c_str(&list->name); + ncplane_putnstr_yx(p, i, 0, max_width, c_str); + al_free(c_str); + struct lia_list *impl = &list->impl; + s32 current = (s32)impl->current; + s32 index = AL_MAX(current - (entries_per_list / 2), 0); + s32 size = (s32)impl->entries.size; + s32 end = AL_MIN(index + entries_per_list, size); + for (s32 j = index; j < end; j++) { + struct lia_list_entry *entry = al_array_at(impl->entries, j); + c_str = al_str_to_c_str(&entry->name); + u32 y = i + (j - index) + 1; + if (j == current) { + ncplane_putchar_yx(p, y, 1, '>'); + ncplane_putnstr_yx(p, y, 3, max_width - 3, c_str); + } else { + ncplane_putnstr_yx(p, y, 1, max_width - 1, c_str); + } + al_free(c_str); + if (++current_line >= max_height) break; + } + } +} + +static void layout_nodes(struct cmsrv_ui *ui, struct ncplane *parent) +{ + struct ncplane_options nopts = { + .rows = ui->term_rows - (ui->term_rows / LOG_RATIO), + .cols = ui->term_cols, + .x = 0, + .y = 0 + }; + ui->nodes.p = ncplane_create(parent, &nopts); +} + +static void erase_nodes(struct cmsrv_ui *ui) +{ + struct ncplane *p = ui->nodes.p; + ncplane_erase(p); +} + +static void render_nodes(struct cmsrv_ui *ui) +{ + struct ncplane *p = ui->lists.p; + + u32 max_width = ncplane_dim_x(p); + u32 max_height = ncplane_dim_y(p); + (void)max_height; + + char strbuf[16]; + + struct lia_node *node; + al_array_foreach(ui->server->data.server.nodes, i, node) { + al_snprintf(strbuf, sizeof(strbuf), "%hu", node->id); + ncplane_putnstr_yx(p, i, 0, max_width, strbuf); + struct lia_node_connection *conn; + al_array_foreach(node->connections, j, conn) { + al_snprintf(strbuf, sizeof(strbuf), "%u", conn->pool.ready.size); + ncplane_putnstr_yx(p, i + j + 1, 2, max_width, strbuf); + } + } +} + +void cmsrv_ui_render(struct cmsrv_ui *ui) +{ + if (ui->pending_layout) { + ui->pending_layout = false; + struct ncplane *stdplane = notcurses_stdplane(ui->nc); + layout_log(ui, stdplane); + layout_lists(ui, stdplane); + layout_nodes(ui, stdplane); + } + erase_log(ui); + erase_lists(ui); + erase_nodes(ui); + render_log(ui); + switch (ui->pane) { + case CMSRV_UI_LISTS: + render_lists(ui); + break; + case CMSRV_UI_RESOURCES: + break; + case CMSRV_UI_NODES: + render_nodes(ui); + break; + } + notcurses_render(ui->nc); +} + +void cmsrv_ui_close(struct cmsrv_ui *ui) +{ + notcurses_stop(ui->nc); +} diff --git a/src/fruits/cmsrv/ui.h b/src/fruits/cmsrv/ui.h new file mode 100644 index 0000000..08ad956 --- /dev/null +++ b/src/fruits/cmsrv/ui.h @@ -0,0 +1,44 @@ +#include +#include +#include + +#include "../../server/server.h" + +enum { + CMSRV_UI_LISTS = 0, + CMSRV_UI_RESOURCES, + CMSRV_UI_NODES +}; + +struct cmsrv_ui { + struct notcurses *nc; + u32 term_cols; + u32 term_rows; + bool pending_layout; + u8 pane; + struct { + struct ncplane *p; + array(char *) messages; + } log; + struct { + struct ncplane *p; + } lists; + struct { + } resources; + struct { + struct ncplane *p; + } nodes; + struct camu_server *server; +}; + +bool cmsrv_ui_init(struct cmsrv_ui *ui, struct camu_server *server); + +s32 cmsrv_ui_get_input_fd(struct cmsrv_ui *ui); +bool cmsrv_ui_read_input(struct cmsrv_ui *ui, struct ncinput *input); + +void cmsrv_ui_set_pane(struct cmsrv_ui *ui, u8 pane); + +void cmsrv_ui_push_message(struct cmsrv_ui *ui, char *message); +void cmsrv_ui_render(struct cmsrv_ui *ui); + +void cmsrv_ui_close(struct cmsrv_ui *ui); diff --git a/src/fruits/cmv/cmv.c b/src/fruits/cmv/cmv.c new file mode 100644 index 0000000..8eaa8b7 --- /dev/null +++ b/src/fruits/cmv/cmv.c @@ -0,0 +1,151 @@ +#include +#include +#include + +#include "../../sink/desktop.h" +#include "../../server/common.h" +#include "../../codec/ffmpeg/common.h" +#ifndef CAMU_SINK_ONLY +#include "../../server/server.h" +#include "../../server/list.h" +#else +#include "../../server/common.c" +#endif + +struct cmv { + struct aki_event_loop loop; + struct camu_desktop desktop; +#ifndef CAMU_SINK_ONLY + struct camu_server server; +#endif +}; + +#ifndef CAMU_SINK_ONLY +static void meta_callback(void *userdata, u8 op, struct lia_list_entry *entry) +{ + struct cmv *c = (struct cmv *)userdata; + (void)c; + switch (op) { + case LIANA_META_PLAYING: { + struct camu_server_resource *resource = (struct camu_server_resource *)entry->opaque; + al_log_info("server", "now playing: %.*s.", AL_STR_PRINTF(&resource->unique_id)); + break; + } + } +} + +static void exit_callback(void *userdata, struct camu_desktop *desktop) +{ + struct cmv *c = (struct cmv *)userdata; + (void)desktop; + camu_server_close(&c->server); +} +#endif + +static aki_thread_result AKI_THREADCALL event_loop_thread(void *userdata) +{ + struct cmv *c = (struct cmv *)userdata; + aki_event_loop_run(&c->loop); + return 0; +} + +static struct cmv c = { 0 }; + +static void sigint_handler(int signum) +{ + (void)signum; + c.desktop.should_quit = 1; +} + +#define failure() do { \ + goto out; \ +} while (0) + +#ifndef _WIN32 +s32 main(s32 argc, char *argv[]) +#else +s32 wmain(s32 argc, wchar_t **argv) +#endif +{ + if (!aki_common_init() || !stl_global_init(false)) { + return EXIT_FAILURE; + } + + s32 ret = EXIT_FAILURE; + + signal(SIGINT, sigint_handler); + + camu_ff_set_default_log_callback(); + + aki_event_loop_init(&c.loop); + + u8 type; + str *addr; +#ifndef CAMU_SINK_ONLY + bool local = argc > 1; + if (local) { + type = AKI_SOCKET_UNIX; addr = CAMU_UNIX_LOCAL; + if (!camu_server_init(&c.server, type, &c.loop)) failure(); + if (!camu_server_listen(&c.server, addr, CAMU_PORT)) failure(); + struct camu_list *list = al_array_at(c.server.lists, 0); + list->impl.callback = meta_callback; + list->impl.userdata = &c; + } else { + type = CAMU_LOCAL_TYPE; + addr = CAMU_LOCAL_ADDR; + } +#else + type = AKI_SOCKET_TCP; addr = CAMU_SERVER_IP; +#endif + +#ifndef CAMU_SINK_ONLY + if (local) { + for (s32 i = 1; i < argc; i++) { +#ifndef _WIN32 + str arg = *al_str_cr(argv[i]); +#else + str arg; + al_wstr_to_str(al_wstr_cr(argv[i]), &arg); +#endif + struct aki_packet *packet = aki_packet_create(); + aki_packet_write_str(packet, &arg); + camu_server_local_add(&c.server, packet); + } + } +#else + (void)argc; + (void)argv; +#endif + + if (!camu_desktop_init(&c.desktop, "cmv")) failure(); +#ifndef CAMU_SINK_ONLY + if (local) { + c.desktop.exit_callback = exit_callback; + c.desktop.userdata = &c; + } +#endif + if (!camu_desktop_connect(&c.desktop, type, &c.loop, addr, CAMU_PORT)) failure(); + + struct aki_thread thread0; + aki_thread_create(&thread0, event_loop_thread, &c); + + while (camu_desktop_tick(&c.desktop)) {} + + camu_desktop_stop(&c.desktop); + aki_thread_join(&thread0); + camu_desktop_free(&c.desktop); +#ifndef CAMU_SINK_ONLY + camu_server_free(&c.server); +#endif + + ret = EXIT_SUCCESS; +out: + aki_event_loop_destroy(&c.loop); + + camu_ff_free_default_log_callback(); + + stl_global_close(); + aki_common_close(); + + return ret; +} diff --git a/src/fruits/cmv/meson.build b/src/fruits/cmv/meson.build new file mode 100644 index 0000000..ba571c3 --- /dev/null +++ b/src/fruits/cmv/meson.build @@ -0,0 +1,25 @@ +cmv_src = ['cmv.c'] +cmv_deps = [desktop] +cmv_args = [] +if get_option('sink-only') + cmv_args += ['-DCAMU_SINK_ONLY'] +else + cmv_deps += [server] +endif + +if get_option('portal').enabled() + cmv_deps += [portal] +endif + +use_tui = false +if use_tui + cmv_src += [] + cmv_deps += [dependency('notcurses')] +endif + +cmv_link_args = [] +if is_windows and meson.is_cross_build() + cmv_link_args += ['-static', '-static-libgcc', '-static-libstdc++', '-municode', '-mwindows'] +endif + +executable('cmv', cmv_src, dependencies: cmv_deps, c_args: cmv_args, link_args: cmv_link_args) diff --git a/src/fruits/sink/meson.build b/src/fruits/sink/meson.build deleted file mode 100644 index 1dd9140..0000000 --- a/src/fruits/sink/meson.build +++ /dev/null @@ -1,15 +0,0 @@ -sink_src = ['sink.c'] -sink_deps = [common_deps, buffer, render, screen, mixer, libsink] -sink_args = [] - -use_tui = false -if use_tui - sink_src += [] - sink_deps += [dependency('notcurses')] -endif - -if is_windows and meson.is_cross_build() - sink_args += ['-static', '-static-libgcc', '-static-libstdc++', '-municode', '-mwindows'] -endif - -executable('sink', sink_src, dependencies: sink_deps, link_args: sink_args) diff --git a/src/fruits/sink/sink.c b/src/fruits/sink/sink.c deleted file mode 100644 index 2b3d451..0000000 --- a/src/fruits/sink/sink.c +++ /dev/null @@ -1,208 +0,0 @@ -#include -#include - -#include "../../mixer/audio_miniaudio.h" - -#include "../../screen/screen.h" -#include "../../render/renderer_libplacebo.h" -//#include "../../render/renderer_tiger.h" - -#include "../../libsink/sink.h" -#include "../../server/common.h" - -struct cmv { - s32 quit; - struct camu_screen scr; - struct camu_renderer *renderer; - struct camu_mixer mixer; - struct aki_event_loop loop; - struct camu_sink sink; -}; - -static u8 sink_callback(void *userdata, u8 op, u8 type, void *opaque) -{ - struct cmv *c = (struct cmv *)userdata; - switch (op) { - case CAMU_SINK_ADD_BUFFER: - switch (type) { - case CAMU_SINK_AUDIO: { - struct camu_audio_buffer *buf = (struct camu_audio_buffer *)opaque; - camu_mixer_add_buffer(&c->mixer, buf); - al_log_debug("cmv", "Audio buffer added."); - break; - } - case CAMU_SINK_VIDEO: { - struct camu_video_buffer *buf = (struct camu_video_buffer *)opaque; - camu_screen_add_buffer(&c->scr, buf); - al_log_debug("cmv", "Video buffer added."); - break; - } - } - break; - case CAMU_SINK_REMOVE_BUFFER: - switch (type) { - case CAMU_SINK_AUDIO: { - struct camu_audio_buffer *buf = (struct camu_audio_buffer *)opaque; - camu_mixer_remove_buffer(&c->mixer, buf); - al_log_debug("cmv", "Audio buffer removed."); - break; - } - case CAMU_SINK_VIDEO: { - struct camu_video_buffer *buf = (struct camu_video_buffer *)opaque; - camu_screen_remove_buffer(&c->scr, buf); - al_log_debug("cmv", "Video buffer removed."); - break; - } - } - break; - case CAMU_SINK_SWAP_BUFFER: - switch (type) { - case CAMU_SINK_AUDIO: { - break; - } - } - break; - case CAMU_SINK_SET_BUFFERED: { - break; - } - case CAMU_SINK_START: - switch (type) { - case CAMU_SINK_AUDIO: { - camu_mixer_resume(&c->mixer); - al_log_debug("cmv", "Audio started."); - break; - } - case CAMU_SINK_VIDEO: { - camu_screen_set_state(&c->scr, CAMU_SCREEN_PLAYING); - camu_screen_wake(&c->scr); - al_log_debug("cmv", "Video started."); - break; - } - } - break; - case CAMU_SINK_STOP: - switch (type) { - case CAMU_SINK_AUDIO: { - camu_mixer_pause(&c->mixer); - al_log_debug("cmv", "Audio stopped."); - break; - } - case CAMU_SINK_VIDEO: { - camu_screen_set_state(&c->scr, CAMU_SCREEN_PAUSED); - al_log_debug("cmv", "Video stopped."); - break; - } - } - break; - case CAMU_SINK_EXIT: - camu_sink_close(&c->sink); - break; - } - return CAMU_SINK_OK; -} - -static void screen_callback(void *userdata, u8 op, f64 float0) -{ - struct cmv *c = (struct cmv *)userdata; - switch (op) { - case CAMU_SCREEN_NEXT: - camu_sink_skip(&c->sink, 1); - break; - case CAMU_SCREEN_PREVIOUS: - camu_sink_skip(&c->sink, -1); - break; - case CAMU_SCREEN_TOGGLE_PAUSE: - camu_sink_toggle_pause(&c->sink); - break; - case CAMU_SCREEN_SEEK: - camu_sink_seek(&c->sink, float0); - break; - case CAMU_SCREEN_RESEEK: - camu_sink_reseek(&c->sink); - break; - case CAMU_SCREEN_CLOSE: - c->quit = 1; - break; - } -} - -static aki_thread_result AKI_THREADCALL event_loop_thread(void *userdata) -{ - struct cmv *c = (struct cmv *)userdata; - aki_event_loop_init(&c->loop); - camu_sink_init(&c->sink, &c->loop, &c->mixer, c->renderer); - c->sink.callback = sink_callback; - c->sink.userdata = c; - if (camu_sink_connect(&c->sink, CAMU_SERVER_IP, CAMU_PORT)) { - aki_event_loop_run(&c->loop); - } - aki_event_loop_destroy(&c->loop); - return 0; -} - -static struct cmv c = { 0 }; - -static void sigint_handler(s32 signum) -{ - (void)signum; - c.quit = 1; -} - -#ifndef _WIN32 -s32 main(s32 argc, char *argv[]) -#else -s32 wmain(s32 argc, wchar_t **argv) -#endif -{ - (void)argc; - (void)argv; - if (!aki_common_init()) return EXIT_FAILURE; - - signal(SIGINT, sigint_handler); - - c.scr.callback = screen_callback; - c.scr.userdata = &c; - if (!camu_screen_init(&c.scr) || !camu_screen_create_window(&c.scr, "cmv")) { - goto err; - } - c.renderer = camu_renderer_lp_create(); - //c.renderer = camu_renderer_tiger_create(); - if (!camu_screen_create_renderer(&c.scr, c.renderer)) { - goto err; - } - c.renderer->render(c.renderer, &c.scr); - - if (!camu_mixer_init(&c.mixer, (struct camu_audio *)&audio_plugin_miniaudio)) { - goto err; - } - - struct aki_thread thread0; - aki_thread_create(&thread0, event_loop_thread, &c); - - c.quit = 0; - - while (!c.quit) { - if (camu_screen_tick(&c.scr)) { - c.renderer->render(c.renderer, &c.scr); - } - } - - camu_screen_set_state(&c.scr, CAMU_SCREEN_CLOSED); - - camu_sink_stop(&c.sink); - - aki_thread_join(&thread0); - - camu_sink_free(&c.sink); - - camu_mixer_close(&c.mixer); - c.renderer->free(&c.renderer); - camu_screen_close(&c.scr); - - aki_common_close(); - - return EXIT_SUCCESS; -err: - aki_common_close(); - return EXIT_FAILURE; -} -- cgit v1.2.3-101-g0448