summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/fruits/cmc/cli.h4
-rw-r--r--src/fruits/cmc/cmc.c15
-rw-r--r--src/fruits/cmc/cmc.h1
-rw-r--r--src/fruits/cmc/meson.build9
-rw-r--r--src/fruits/cmc/ui/pane_list.c408
-rw-r--r--src/fruits/cmc/ui/pane_log.c44
-rw-r--r--src/fruits/cmc/ui/pane_search.c226
-rw-r--r--src/fruits/cmc/ui/panes/list.c688
-rw-r--r--src/fruits/cmc/ui/panes/list.h35
-rw-r--r--src/fruits/cmc/ui/panes/log.c65
-rw-r--r--src/fruits/cmc/ui/panes/log.h18
-rw-r--r--src/fruits/cmc/ui/panes/search.c226
-rw-r--r--src/fruits/cmc/ui/ui.c152
-rw-r--r--src/fruits/cmc/ui/ui.h14
-rw-r--r--src/fruits/cmc/ui/util/notcurses_ext.h (renamed from src/fruits/cmc/ui/ext.h)0
-rw-r--r--src/fruits/cmc/ui/util/waveform.c (renamed from src/fruits/cmc/ui/waveform.c)136
-rw-r--r--src/fruits/cmc/ui/util/waveform.h (renamed from src/fruits/cmc/ui/waveform.h)2
-rw-r--r--src/fruits/cmc/ui/widgets/now_playing.c223
-rw-r--r--src/fruits/cmc/ui/widgets/now_playing.h25
-rw-r--r--src/fruits/cmc/ui/widgets/tile.h (renamed from src/fruits/cmc/ui/tile.h)0
-rw-r--r--src/liana/server.c2
-rw-r--r--src/libsink/sink.c11
-rw-r--r--src/mixer/mixer.c2
-rw-r--r--src/screen/screen.c8
24 files changed, 1467 insertions, 847 deletions
diff --git a/src/fruits/cmc/cli.h b/src/fruits/cmc/cli.h
deleted file mode 100644
index 4373054..0000000
--- a/src/fruits/cmc/cli.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#pragma once
-
-struct cmc_cli {
-};
diff --git a/src/fruits/cmc/cmc.c b/src/fruits/cmc/cmc.c
index 265550f..43f1144 100644
--- a/src/fruits/cmc/cmc.c
+++ b/src/fruits/cmc/cmc.c
@@ -66,6 +66,7 @@ static void parse_initial_user_state(struct cmc *c, struct nn_packet *packet)
nn_packet_read_str(packet, &s);
al_str_clone(&list->name, &s);
list->current = nn_packet_read_s32(packet);
+ list->meta_changed = false;
al_array_init(list->entries);
u32 inner_count = nn_packet_read_s32(packet);
for (u32 j = 0; j < inner_count; j++) {
@@ -105,11 +106,11 @@ static void client_callback(void *userdata, u8 op, void *opaque)
}
struct cmc_list *list;
al_array_foreach(c->lists, i, list) {
- cmc_lp_add_list(&c->ui, list);
+ cmc_list_pane_add_list(&c->ui.lists, list);
}
struct cmc_search *search;
al_array_foreach(c->searches, i, search) {
- cmc_sp_add_search(&c->ui, search);
+ //cmc_sp_add_search(&c->ui, search);
}
break;
case CLI_ADD: {
@@ -137,7 +138,7 @@ static void client_callback(void *userdata, u8 op, void *opaque)
search->id = *(s32 *)opaque;
al_array_init(search->pages);
al_array_push(c->searches, search);
- cmc_sp_add_search(&c->ui, search);
+ //cmc_sp_add_search(&c->ui, search);
break;
}
case CAMU_CLIENT_GOT_RESULTS: {
@@ -149,6 +150,7 @@ static void client_callback(void *userdata, u8 op, void *opaque)
str name;
nn_packet_read_str(packet, &name);
struct cmc_list *list = get_list_by_name(c, &name);
+ list->meta_changed = true;
if (!list) break;
switch (op) {
case LIANA_META_ADDED_ENTRY: {
@@ -177,8 +179,9 @@ static void client_callback(void *userdata, u8 op, void *opaque)
for (u32 i = 0; i < count; i++) {
u32 id = nn_packet_read_u32(packet);
al_array_foreach(list->entries, j, entry) {
- if (i != j && entry->id == id) {
- SWAP(al_array_at(list->entries, i), al_array_at(list->entries, j));
+ if (entry->id == id) {
+ if (i != j) SWAP(al_array_at(list->entries, i), al_array_at(list->entries, j));
+ break;
}
}
}
@@ -214,6 +217,8 @@ static void client_callback(void *userdata, u8 op, void *opaque)
entry->raw.data = al_malloc(size);
al_memcpy(entry->raw.data, ptr, size);
entry->raw.sample_count = nn_packet_read_u32(packet);
+ cmc_ui_queue_render(&c->ui);
+ break;
}
}
break;
diff --git a/src/fruits/cmc/cmc.h b/src/fruits/cmc/cmc.h
index ea714dc..817b8b0 100644
--- a/src/fruits/cmc/cmc.h
+++ b/src/fruits/cmc/cmc.h
@@ -41,6 +41,7 @@ struct cmc_list_entry {
struct cmc_list {
str name;
s32 current;
+ bool meta_changed;
array(struct cmc_list_entry *) entries;
};
diff --git a/src/fruits/cmc/meson.build b/src/fruits/cmc/meson.build
index a0fa097..864af8a 100644
--- a/src/fruits/cmc/meson.build
+++ b/src/fruits/cmc/meson.build
@@ -10,10 +10,11 @@ use_tui = true
if use_tui
cmc_src += [
'ui/ui.c',
- 'ui/pane_list.c',
- 'ui/pane_search.c',
- 'ui/pane_log.c',
- 'ui/waveform.c'
+ 'ui/panes/list.c',
+ 'ui/panes/search.c',
+ 'ui/panes/log.c',
+ 'ui/widgets/now_playing.c',
+ 'ui/util/waveform.c'
]
cmc_deps += [notcurses, notcurses_core, codecs_client]
endif
diff --git a/src/fruits/cmc/ui/pane_list.c b/src/fruits/cmc/ui/pane_list.c
deleted file mode 100644
index b62e0a6..0000000
--- a/src/fruits/cmc/ui/pane_list.c
+++ /dev/null
@@ -1,408 +0,0 @@
-#include <nnwt/thread.h>
-
-#include "../../liana/list.h"
-#include "../../sink/util.h"
-
-#include "../cmc.h"
-
-#include "ui.h"
-#include "ext.h"
-#include "waveform.h"
-
-void cmc_lp_init(struct cmc_ui *ui)
-{
- al_array_init(ui->lp.tabs);
- ui->lp.current = 0;
-}
-
-void cmc_lp_layout(struct cmc_ui *ui, struct ncplane *parent)
-{
- if (ui->lp.n) ncplane_destroy(ui->lp.n);
- struct ncplane_options nopts = { 0 };
- nopts.rows = ncplane_dim_y(parent);
- nopts.cols = ncplane_dim_x(parent);
- ui->lp.n = ncplane_create(parent, &nopts);
-}
-
-void cmc_lp_add_list(struct cmc_ui *ui, struct cmc_list *list)
-{
- struct cmc_list_tab tab = {
- .list = list,
- .selected = list->current,
- .leader_pressed = false
- };
- al_array_push(ui->lp.tabs, tab);
-}
-
-bool cmc_lp_handle_input(struct cmc_ui *ui, struct ncinput *input)
-{
- if (!ui->lp.tabs.count) return false;
-
- struct cmc_list_tab *tab = &al_array_at(ui->lp.tabs, ui->lp.current);
- s32 count = (s32)tab->list->entries.count;
- bool leader = tab->leader_pressed;
- if (cmc_ui_consider_input(input, false)) {
- tab->leader_pressed = false;
- }
-
- switch (input->id) {
- case 'g':
- CONSIDER_INPUT();
- if (leader) {
- if (tab->move.active) {
- tab->move.offset = -tab->move.selected;
- } else {
- tab->selected = 0;
- }
- } else {
- tab->leader_pressed = true;
- }
- break;
- case 'G':
- CONSIDER_INPUT();
- if (tab->move.active) {
- tab->move.offset = count - 1 - tab->move.selected;
- } else {
- tab->selected = count - 1;
- }
- break;
- case 'f':
- //CONSIDER_INPUT_MOD_CTRL();
- CONSIDER_INPUT_REPEAT();
- if (tab->move.active) {
- if (tab->selected + tab->move.offset < count - tab->page_length) {
- tab->move.offset += tab->page_length;
- } else {
- tab->move.offset = count - 1 - tab->move.selected;
- }
- } else {
- if (tab->selected < count - tab->page_length) {
- tab->selected += tab->page_length;
- } else {
- tab->selected = count - 1;
- }
- }
- break;
- case 'b':
- //CONSIDER_INPUT_MOD_CTRL();
- CONSIDER_INPUT_REPEAT();
- if (tab->move.active) {
- if (tab->selected + tab->move.offset > tab->page_length) {
- tab->move.offset -= tab->page_length;
- } else {
- tab->move.offset = -tab->selected;
- }
- } else {
- if (tab->selected > tab->page_length) {
- tab->selected -= tab->page_length;
- } else {
- tab->selected = 0;
- }
- }
- break;
- case 'j':
- CONSIDER_INPUT_REPEAT();
- if (tab->move.active) {
- if (tab->selected + tab->move.offset < count - 1) {
- tab->move.offset++;
- }
- } else {
- if (tab->selected < count - 1) {
- tab->selected++;
- }
- }
- break;
- case 'k':
- CONSIDER_INPUT_REPEAT();
- if (tab->move.active) {
- if (tab->selected + tab->move.offset > 0) {
- tab->move.offset--;
- }
- } else {
- if (tab->selected > 0) {
- tab->selected--;
- }
- }
- break;
- case 'i':
- CONSIDER_INPUT();
- if (!tab->move.active) {
- tab->move.active = true;
- tab->move.selected = tab->selected;
- tab->move.offset = 0;
- }
- break;
- case NCKEY_ESC:
- CONSIDER_INPUT();
- if (tab->move.active) {
- tab->move.active = false;
- }
- break;
- case NCKEY_RETURN:
- CONSIDER_INPUT();
- if (tab->move.active) {
- tab->move.active = false;
- } else if (tab->selected != tab->list->current) {
- camu_client_skipto(&ui->c->client, &tab->list->name, tab->selected);
- }
- break;
- case NCKEY_BUTTON1:
- CONSIDER_INPUT();
- if (input->x < 0 || input->y < 0) return false;
- u32 x = (u32)input->x;
- u32 y = (u32)input->y;
- ui->last_mouse_x = x;
- ui->last_mouse_y = y;
- u64 now = nn_get_timestamp();
- u64 last = ui->last_mouse_ts;
- if (last != (u64)-1 && now - last < 100000) {
- return false;
- }
- ui->last_mouse_ts = now;
- u32 width = ncplane_dim_x(ui->lp.n);
- u32 height = ncplane_dim_y(ui->lp.n);
- if (x >= 2 && x <= width && y > height - 3) { // @TODO: Make this defined, more to nowplaying.
- struct cmc_list_entry *entry = al_array_at(tab->list->entries, tab->list->current);
- u64 pos = ((x - 2) / (f64)(width - 2)) * entry->duration;
- camu_client_seek(&ui->c->client, &tab->list->name, entry->id, pos);
- }
- break;
- }
-
- return true;
-}
-
-void cmc_lp_erase(struct cmc_ui *ui)
-{
- ncplane_erase(ui->lp.n);
-}
-
-static void render_list_entries(struct cmc_ui *ui, struct cmc_list_tab *tab, s32 max_height)
-{
- struct cmc_list *list = tab->list;
- struct ncplane *n = ui->lp.n;
- s32 tracked = tab->move.active ? tab->move.selected + tab->move.offset : tab->selected;
- s32 count = (s32)list->entries.count;
- s32 midpoint = max_height / 2;
- s32 rounded = (max_height + 1) / 2;
- s32 top, end;
- if (tracked < midpoint) {
- top = 0;
- end = MIN(top + max_height, count);
- } else if (tracked + rounded >= count) {
- top = count - max_height;
- end = count;
- } else {
- top = tracked - midpoint;
- end = tracked + rounded;
- }
- s32 index;
- struct cmc_list_entry *entry;
- for (s32 i = top; i < end; i++) {
- index = i;
- if (tab->move.active) {
- if (index == tracked) {
- index = tab->move.selected;
- } else {
- if (index > tracked) index--;
- if (index >= tab->move.selected) index++;
- }
- }
- entry = al_array_at(list->entries, index);
- bool on_current = list->current >= 0 && index == list->current;
- bool styled = i == tracked;
- if (styled) {
- ncplane_set_styles(n, NCSTYLE_BOLD);
- ncplane_set_fg_palindex(n, 4);
- }
- s32 y = i - top;
- u32 length = entry->name.length;
- if (tab->move.active && i == tracked) {
- cmc_ui_putnwstr_yx(n, y, 0, length, &entry->name);
- } else if (on_current) {
- ncplane_putchar_yx(n, y, 1, '>');
- cmc_ui_putnwstr_yx(n, y, 3, length, &entry->name);
- } else {
- cmc_ui_putnwstr_yx(n, y, 1, length, &entry->name);
- }
- if (styled) {
- ncplane_set_styles(n, NCSTYLE_NONE);
- ncplane_set_fg_default(n);
- }
- }
-}
-
-static void render_now_playing(struct cmc_ui *ui, struct cmc_list_tab *tab, u32 y, u32 height)
-{
- struct ncplane *n = ui->lp.n;
- u32 width = ncplane_dim_x(n);
-
- struct cmc_list_entry *entry = NULL;
- if (tab->list->entries.count > (u32)tab->list->current) {
- entry = al_array_at(tab->list->entries, tab->list->current);
- }
-
- u64 pos = 0;
- u64 duration = 0;
- u64 remaining = 0;
- u32 y_off = height - 7;
-
- ncplane_set_styles(n, NCSTYLE_BOLD);
- if (entry) {
- pos = entry->offset;
- duration = entry->duration;
- u64 now = nn_get_timestamp();
- bool paused = entry->start == LIANA_TIMESTAMP_INVALID;
- if (!paused && now > entry->start) {
- pos += now - entry->start;
- }
- pos = MIN(pos, duration);
- remaining = duration - pos;
- //ncplane_putstr_yx(n, y_off, 2, "▸");
- //ncplane_putstr_yx(n, y_off, 2, "▶");
- //ncplane_putstr_yx(n, y_off, 2, "⏵ ");
- //ncplane_putstr_yx(n, y_off, 2, "|");
- //ncplane_putstr_yx(n, y_off, 2, "|");
- //ncplane_putstr_yx(n, y_off, 2, "⏵");
- //ncplane_putstr_yx(n, y_off, 2, "⏸");
- if (remaining == 0) {
- ncplane_putstr_yx(n, y_off - 1, 2, "∨");
- } else {
- ncplane_putstr_yx(n, y_off - 1, 2, paused ? "^": ">");
- }
- } else {
- ncplane_putstr_yx(n, y_off - 1, 2, "∨");
- }
- ncplane_set_styles(n, NCSTYLE_NONE);
-
- f32 percent = 1.f;
- if (duration > 0) percent = MIN(pos / (f32)duration, 1.f);
- for (u32 i = 0; i < (width - 2) * percent; i++) {
- ncplane_putchar_yx(n, y_off, i + 1, '-');
- }
-
- y_off--;
-
- char timestr[16] = { 0 }; // max = 829128280:01:49\0
- s32 offset = camu_print_time(timestr, sizeof(timestr), pos, true, 4);
- ncplane_putstr_yx(n, y_off, 4, timestr);
- ncplane_putstr_yx(n, y_off, 4 + offset, "⎹");
-
- if (entry) {
- cmc_ui_putnwstr_yx(n, y_off, 6 + offset, MIN(entry->name.length, (u32)60), &entry->name);
- }
-
- bool show_remaining = false;
- if (show_remaining) {
- offset = camu_print_time(timestr, sizeof(timestr), remaining, true, 4);
- } else {
- offset = camu_print_time(timestr, sizeof(timestr), duration, true, 4);
- }
- ncplane_putstr_yx(n, y_off, width - 4 - offset, "⎸");
- // "=" Duration, "+" Segment, "-" Remaining.
- if (show_remaining) {
- ncplane_putstr_yx(n, y_off, width - 3 - offset, "-");
- } else {
- ncplane_putstr_yx(n, y_off, width - 3 - offset, "=");
- }
- ncplane_putstr_yx(n, y_off, width - 2 - offset, timestr);
-
- /*
- ncplane_putchar_yx(n, height - 5, 2, 'A');
- ncplane_putchar_yx(n, height - 4, 2, 'A');
- ncplane_putchar_yx(n, height - 3, 2, 'A');
- ncplane_putchar_yx(n, height - 2, 2, 'A');
- */
-
- if (entry && entry->raw.data != NULL) {
- u32 length = width - 2;
- u32 top = height - 6;
- u32 wave_height = 5;
- if (entry->raw.drawing == NULL || entry != ui->lp.blitted) {
- entry->raw.drawing = al_malloc(sizeof(char **) * wave_height);
- for (u32 j = 0; j < wave_height; j++) {
- entry->raw.drawing[j] = al_malloc(sizeof(char *) * length);
- for (u32 i = 0; i < length; i++) {
- entry->raw.drawing[j][i] = al_calloc(1, 6);
- }
- }
- if (!ui->lp.w) {
- struct ncplane_options opts = { 0 };
- opts.x = 1;
- opts.y = top;
- opts.cols = length;
- opts.rows = wave_height;
- ui->lp.w = ncplane_create(n, &opts);
- }
- /*
- struct nccell ncl;
- nccell_init(&ncl);
- nccell_load(w, &ncl, " ");
- nccell_set_bg_palindex(&ncl, 3);
- ncplane_polyfill_yx(w, 0, 0, &ncl);
- */
- u32 *rgba;
- u32 pixel_width, pixel_height;
- ncplane_pixel_geom(ui->lp.w, &pixel_height, &pixel_width, NULL, NULL, NULL, NULL);
- //cmc_draw_waveform(entry, length, wave_height, false, entry->raw.drawing, &rgba);
- cmc_draw_waveform(entry, pixel_width, pixel_height, false, entry->raw.drawing, &rgba);
- if (ui->lp.vis) {
- ncvisual_destroy(ui->lp.vis);
- }
- ui->lp.vis = ncvisual_from_rgba(rgba, pixel_height, pixel_width * 4, pixel_width);
- }
-
- if (entry != ui->lp.blitted) {
- ncplane_erase(ui->lp.w);
- struct ncvisual_options vopts = { 0 };
- vopts.blitter = NCBLIT_PIXEL;
- vopts.scaling = NCSCALE_STRETCH;
- vopts.flags = NCVISUAL_OPTION_NOINTERPOLATE;
- vopts.n = ui->lp.w;
- vopts.x = 0;
- vopts.y = 0;
- ncvisual_blit(ui->nc, ui->lp.vis, &vopts);
- ui->lp.blitted = entry;
- }
-
- /*
- ncplane_set_fg_palindex(n, 5);
- for (u32 j = 0; j < wave_height; j++) {
- for (u32 i = 0; i < length; i++) {
- char *cell = entry->raw.drawing[j][i];
- if (cell[0] == 'R') {
- ncplane_set_bg_palindex(n, 2);
- }
- ncplane_putstr_yx(n, top + j, 1 + i, cell + 1);
- if (cell[0] == 'R') {
- ncplane_set_bg_default(n);
- }
- }
- }
- ncplane_set_fg_default(n);
- */
- }
-
- /*
- char buf[24];
- al_snprintf(buf, sizeof(buf), "%u, %u | %u, %u", ui->last_mouse_x, ui->last_mouse_y, width, height);
- ncplane_putstr_yx(n, height - 5, 5, buf);
- */
-
- u64 c = 0;
- ncchannels_set_fg_default(&c);
- ncplane_cursor_move_yx(n, y, 0);
- ncplane_light_box(n, NCSTYLE_NONE, c, height - 1, width - 1, 0);
-}
-
-void cmc_lp_render(struct cmc_ui *ui)
-{
- u32 height = ncplane_dim_y(ui->lp.n);
-
- if (ui->lp.tabs.count <= ui->lp.current) return;
- struct cmc_list_tab *tab = &al_array_at(ui->lp.tabs, ui->lp.current);
-
- tab->page_length = height - 9;
- render_list_entries(ui, tab, tab->page_length);
- render_now_playing(ui, tab, tab->page_length, height);
-}
diff --git a/src/fruits/cmc/ui/pane_log.c b/src/fruits/cmc/ui/pane_log.c
deleted file mode 100644
index a35cf20..0000000
--- a/src/fruits/cmc/ui/pane_log.c
+++ /dev/null
@@ -1,44 +0,0 @@
-#include "ui.h"
-
-void cmc_logp_init(struct cmc_ui *ui)
-{
- al_array_init(ui->logp.messages);
-}
-
-void cmc_logp_layout(struct cmc_ui *ui, struct ncplane *parent)
-{
- if (ui->logp.n) ncplane_destroy(ui->logp.n);
- struct ncplane_options nopts = { 0 };
- nopts.rows = ncplane_dim_y(parent);
- nopts.cols = ncplane_dim_x(parent);
- ui->logp.n = ncplane_create(parent, &nopts);
-}
-
-void cmc_logp_push_message(struct cmc_ui *ui, char *message)
-{
- al_array_push(ui->logp.messages, message);
-}
-
-void cmc_logp_erase(struct cmc_ui *ui)
-{
- ncplane_erase(ui->logp.n);
-}
-
-void cmc_logp_render(struct cmc_ui *ui)
-{
- struct ncplane *n = ui->logp.n;
-
- u32 max_height = ncplane_dim_y(n);
-
- u32 size = ui->logp.messages.count;
- u32 index = (size > max_height) ? size - max_height : 0;
- for (u32 i = index; i < size; i++) {
- // We can't use putnstr to control the width because these messages will have wide characters.
- ncplane_putstr_yx(n, i - index, 0, al_array_at(ui->logp.messages, i));
- }
-
- for (u32 i = 0; i < index; i++) {
- al_free(al_array_at(ui->logp.messages, i));
- }
- al_array_remove_range(ui->logp.messages, 0, index);
-}
diff --git a/src/fruits/cmc/ui/pane_search.c b/src/fruits/cmc/ui/pane_search.c
deleted file mode 100644
index 21c9b67..0000000
--- a/src/fruits/cmc/ui/pane_search.c
+++ /dev/null
@@ -1,226 +0,0 @@
-#include "../cmc.h"
-
-#include "ui.h"
-
-AL_ASSERT_TYPE_SIZE(wchar_t, 4);
-
-void cmc_sp_init(struct cmc_ui *ui)
-{
- al_array_init(ui->sp.tabs);
- ui->sp.current = 0;
-}
-
-void cmc_sp_layout(struct cmc_ui *ui, struct ncplane *parent)
-{
- if (ui->sp.n) ncplane_destroy(ui->sp.n);
- struct ncplane_options nopts = { 0 };
- nopts.rows = ncplane_dim_y(parent) - 1;
- nopts.cols = ncplane_dim_x(parent) - 1;
- ui->sp.n = ncplane_create(parent, &nopts);
- struct cmc_search_tab tab = {
- .search = NULL,
- };
- nopts.x = 3;
- nopts.y = 2;
- nopts.rows = 10;
- nopts.cols = ncplane_dim_x(ui->sp.n) - 3;
- tab.input = ncplane_create(ui->sp.n, &nopts);
- tab.cursor = 0;
- tab.input_active = false;
- tab.input_text = al_wstr_null();
- tab.input_changed = false;
- al_array_push(ui->sp.tabs, tab);
-}
-
-void cmc_sp_add_search(struct cmc_ui *ui, struct cmc_search *search)
-{
- struct cmc_search_tab tab = {
- .search = search,
- };
- struct ncplane_options nopts = { 0 };
- nopts.x = 3;
- nopts.y = 2;
- nopts.rows = 10;
- nopts.cols = ncplane_dim_x(ui->sp.n) - 3;
- tab.input = ncplane_create(ui->sp.n, &nopts);
- tab.cursor = 0;
- tab.input_active = false;
- tab.input_text = al_wstr_null();
- tab.input_changed = false;
- al_array_push(ui->sp.tabs, tab);
-}
-
-static bool handle_text_input(struct cmc_search_tab *tab, struct ncinput *input)
-{
- bool entered = false;
-
- switch (input->id) {
- case NCKEY_BACKSPACE:
- if (tab->cursor > 0) {
- al_wstr_remove_at(&tab->input_text, tab->cursor - 1);
- tab->cursor--;
- tab->input_changed = true;
- }
- break;
- case NCKEY_LEFT:
- if (tab->cursor > 0) {
- tab->cursor--;
- }
- break;
- case NCKEY_RIGHT:
- if (tab->cursor < tab->input_text.length) {
- tab->cursor++;
- }
- break;
- case NCKEY_RETURN:
- entered = true;
- // fallthrough
- case NCKEY_ESC:
- tab->input_active = false;
- tab->input_changed = true; // Erase text on next render.
- break;
- default:
- if (ncinput_ctrl_p(input)) {
- switch (input->id) {
- case 'A':
- tab->cursor = 0;
- break;
- case 'E':
- tab->cursor = tab->input_text.length;
- break;
- case 'B':
- if (tab->cursor > 0) {
- tab->cursor--;
- }
- break;
- case 'F':
- if (tab->cursor < tab->input_text.length) {
- tab->cursor++;
- }
- break;
- case 'U':
- for (u32 i = 1; i <= tab->cursor; i++) {
- al_wstr_remove_at(&tab->input_text, tab->cursor - i);
- }
- tab->cursor = 0;
- tab->input_changed = true;
- break;
- }
- break;
- }
-
- if (ncinput_alt_p(input) || nckey_synthesized_p(input->id)) {
- break;
- }
-
- al_wstr_insert(&tab->input_text, tab->cursor, &al_wstr_w((wchar_t *)input->eff_text, 0, 1));
- tab->cursor++;
- tab->input_changed = true;
-
- /*
- // https://github.com/dankamongmen/notcurses/blob/c11efe877f2245901f5c9ce110de7a2c83cfa2ed/src/lib/reader.c#L404
- for (s32 c = 0; input->eff_text[c] != 0; c++){
- uchar egc[5] = { 0 };
- if (notcurses_ucs32_to_utf8(&input->eff_text[c], 1, egc, 4) >= 0) {
- // This probably doesn't work on Windows.
- al_wstr_insert(&tab->input_text, tab->cursor, &al_wstr_w((wchar_t *)egc, 0, 1));
- }
- }
- */
-
- break;
- }
-
- tab->visual_cursor = 0;
- if (tab->cursor > 0) {
- for (u32 i = 0; i < tab->cursor; i++) {
- tab->visual_cursor += al_wchar_width(al_wstr_at(&tab->input_text, i));
- }
- }
-
- return entered;
-}
-
-bool cmc_sp_handle_input(struct cmc_ui *ui, struct ncinput *input)
-{
- struct cmc_search_tab *tab = NULL;
- if (ui->sp.tabs.count > 0) {
- tab = &al_array_at(ui->sp.tabs, ui->sp.current);
- }
-
- if (tab) {
- if (tab->input_active) {
- if (cmc_ui_consider_input(input, true)) {
- if (handle_text_input(tab, input)) {
- str query;
- al_str_from_wstr(&query, &tab->input_text);
- camu_client_create_search(&ui->c->client, &al_str_c("youtube"), &query);
- al_str_free(&query);
- }
- if (!tab->input_active) notcurses_cursor_disable(ui->nc);
- }
- } else {
- switch (input->id) {
- case 'i':
- CONSIDER_INPUT();
- tab->input_active = true;
- tab->input_changed = true;
- s32 absx = ncplane_abs_x(tab->input);
- s32 absy = ncplane_abs_y(tab->input);
- notcurses_cursor_enable(ui->nc, absy, absx + tab->visual_cursor);
- break;
- }
- }
- }
-
- return true;
-}
-
-void cmc_sp_erase(struct cmc_ui *ui)
-{
- ncplane_erase(ui->sp.n);
-}
-
-void cmc_sp_render(struct cmc_ui *ui)
-{
- struct cmc_search_tab *tab = NULL;
- if (ui->sp.tabs.count > 0) {
- tab = &al_array_at(ui->sp.tabs, ui->sp.current);
- }
-
- if (tab) {
- if (tab->input_changed) {
- ncplane_erase(tab->input);
- if (tab->input_active) {
- cmc_ui_putnwstr_yx(tab->input, 0, 0, tab->input_text.length, &tab->input_text);
- }
- tab->input_changed = false;
- }
- if (tab->input_active) {
- s32 absx = ncplane_abs_x(tab->input);
- s32 absy = ncplane_abs_y(tab->input);
- notcurses_cursor_enable(ui->nc, absy, absx + tab->visual_cursor);
- }
- }
-
- /*
- if (ui->overlay_shown) {
- ncplane_erase(ui->oln);
- u32 max_height = ncplane_dim_y(ui->oln) - 2;
- u32 count = ui->c->searches.count;
- u32 index = ui->sp.overlay_index - (ui->sp.overlay_index % max_height);
- u32 end = MIN(index + max_height, count);
- for (u32 i = index; i < end; i++) {
- struct cmc_search *search = al_array_at(ui->c->searches, i);
- if (i == ui->sp.overlay_index) {
- ncplane_set_fg_palindex(ui->oln, 6);
- ncplane_putchar_yx(ui->oln, i - index + 1, 2, '>');
- cmc_ui_putstr_maxwidth_yx(ui->oln, i - index + 1, 4, search->query.length, &search->query);
- } else {
- ncplane_set_fg_default(ui->oln);
- cmc_ui_putstr_maxwidth_yx(ui->oln, i - index + 1, 2, search->query.length, &search->query);
- }
- }
- }
- */
-}
diff --git a/src/fruits/cmc/ui/panes/list.c b/src/fruits/cmc/ui/panes/list.c
new file mode 100644
index 0000000..0e853db
--- /dev/null
+++ b/src/fruits/cmc/ui/panes/list.c
@@ -0,0 +1,688 @@
+#include <nnwt/thread.h>
+
+#include "../../cmc.h"
+
+#include "list.h"
+
+void cmc_list_pane_init(struct cmc_list_pane *lp, struct cmc_ui *ui)
+{
+ al_array_init(lp->tabs);
+ lp->selected = -1;
+ cmc_now_playing_init(&lp->np);
+ lp->ui = ui;
+}
+
+bool cmc_list_pane_layout(struct cmc_list_pane *lp, struct ncplane *parent)
+{
+ if (lp->n) ncplane_destroy(lp->n);
+ struct ncplane_options nopts = { 0 };
+ u32 height = ncplane_dim_y(parent);
+ nopts.rows = height;
+ nopts.cols = ncplane_dim_x(parent);
+ lp->n = ncplane_create(parent, &nopts);
+ lp->page_length = height - 9;
+ cmc_now_playing_layout(&lp->np, lp->n, lp->page_length);
+ return true;
+}
+
+void cmc_list_pane_add_list(struct cmc_list_pane *lp, struct cmc_list *list)
+{
+ struct cmc_list_tab tab = { 0 };
+ tab.list = list;
+ tab.selected = list->current;
+ al_array_push(lp->tabs, tab);
+ if (lp->selected == -1) {
+ lp->selected = lp->tabs.count - 1;
+ }
+}
+
+bool cmc_list_pane_handle_input(struct cmc_list_pane *lp, struct ncinput *input)
+{
+ if (lp->selected == -1) return false;
+
+ struct cmc_list_tab *tab = &al_array_at(lp->tabs, lp->selected);
+ s32 count = (s32)tab->list->entries.count;
+
+ bool leader = tab->leader_pressed;
+ if (cmc_ui_consider_input(input, false)) {
+ tab->leader_pressed = false;
+ }
+
+ switch (input->id) {
+ case 'g':
+ CONSIDER_INPUT();
+ if (leader) {
+ if (tab->move.active) {
+ if (tab->move.offset != -tab->move.selected) {
+ tab->move.offset = -tab->move.selected;
+ return true;
+ }
+ } else if (tab->selected != 0) {
+ tab->selected = 0;
+ return true;
+ }
+ } else {
+ tab->leader_pressed = true;
+ }
+ break;
+ case 'G':
+ CONSIDER_INPUT();
+ if (tab->move.active) {
+ if (tab->move.offset != count - 1 - tab->move.selected) {
+ tab->move.offset = count - 1 - tab->move.selected;
+ return true;
+ }
+ } else if (tab->selected != count - 1) {
+ tab->selected = count - 1;
+ return true;
+ }
+ break;
+ case 'f':
+ CONSIDER_INPUT_REPEAT();
+ if (tab->move.active) {
+ if (tab->selected + tab->move.offset < count - lp->page_length) {
+ tab->move.offset += lp->page_length;
+ return true;
+ } else if (tab->move.offset != count - 1 - tab->move.selected) {
+ tab->move.offset = count - 1 - tab->move.selected;
+ return true;
+ }
+ } else {
+ if (tab->selected < count - lp->page_length) {
+ tab->selected += lp->page_length;
+ return true;
+ } else if (tab->selected != count - 1) {
+ tab->selected = count - 1;
+ return true;
+ }
+ }
+ break;
+ case 'b':
+ CONSIDER_INPUT_REPEAT();
+ if (tab->move.active) {
+ if (tab->selected + tab->move.offset > lp->page_length) {
+ tab->move.offset -= lp->page_length;
+ return true;
+ } else if (tab->move.offset != -tab->selected) {
+ tab->move.offset = -tab->selected;
+ return true;
+ }
+ } else {
+ if (tab->selected > lp->page_length) {
+ tab->selected -= lp->page_length;
+ return true;
+ } else if (tab->selected != 0) {
+ tab->selected = 0;
+ return true;
+ }
+ }
+ break;
+ case 'j':
+ CONSIDER_INPUT_REPEAT();
+ if (tab->move.active) {
+ if (tab->selected + tab->move.offset < count - 1) {
+ tab->move.offset++;
+ return true;
+ }
+ } else {
+ if (tab->selected < count - 1) {
+ tab->selected++;
+ return true;
+ }
+ }
+ break;
+ case 'k':
+ CONSIDER_INPUT_REPEAT();
+ if (tab->move.active) {
+ if (tab->selected + tab->move.offset > 0) {
+ tab->move.offset--;
+ return true;
+ }
+ } else {
+ if (tab->selected > 0) {
+ tab->selected--;
+ return true;
+ }
+ }
+ break;
+ case 'i':
+ CONSIDER_INPUT();
+ if (!tab->move.active) {
+ tab->move.active = true;
+ tab->move.selected = tab->selected;
+ tab->move.offset = 0;
+ return true;
+ }
+ break;
+ case NCKEY_ESC:
+ CONSIDER_INPUT();
+ if (tab->move.active) {
+ tab->move.active = false;
+ return true;
+ }
+ break;
+ case NCKEY_RETURN:
+ CONSIDER_INPUT();
+ if (tab->move.active) {
+ tab->move.active = false;
+ return true;
+ } else if (tab->selected != tab->list->current) {
+ camu_client_skipto(&lp->ui->c->client, &tab->list->name, tab->selected);
+ }
+ break;
+ case NCKEY_BUTTON1:
+ /*
+ CONSIDER_INPUT();
+ if (input->x < 0 || input->y < 0) return false;
+ u32 x = (u32)input->x;
+ u32 y = (u32)input->y;
+ ui->last_mouse_x = x;
+ ui->last_mouse_y = y;
+ u64 now = nn_get_timestamp();
+ u64 last = ui->last_mouse_ts;
+ if (last != (u64)-1 && now - last < 100000) {
+ return false;
+ }
+ ui->last_mouse_ts = now;
+ u32 width = ncplane_dim_x(ui->lp.n);
+ u32 height = ncplane_dim_y(ui->lp.n);
+ if (x >= 2 && x <= width && y > height - 3) { // @TODO: Make this defined, more to nowplaying.
+ struct cmc_list_entry *entry = al_array_at(tab->list->entries, tab->list->current);
+ u64 pos = ((x - 2) / (f64)(width - 2)) * entry->duration;
+ camu_client_seek(&ui->c->client, &tab->list->name, entry->id, pos);
+ }
+ */
+ break;
+ }
+
+ return false;
+}
+
+static struct cmc_list_entry *tab_current_entry(struct cmc_list_tab *tab)
+{
+ if (tab->list->current >= 0) {
+ return al_array_at(tab->list->entries, tab->list->current);
+ }
+ return NULL;
+}
+
+bool cmc_list_pane_tick(struct cmc_list_pane *lp)
+{
+ bool tick = false;
+ if (lp->selected != -1) {
+ struct cmc_list_tab *tab = &al_array_at(lp->tabs, lp->selected);
+ if (tab->list->meta_changed) {
+ tab->list->meta_changed = false;
+ tick = true;
+ }
+ tick |= cmc_now_playing_tick(&lp->np, tab_current_entry(tab));
+ }
+ return tick;
+}
+
+void cmc_list_pane_erase(struct cmc_list_pane *lp)
+{
+ ncplane_erase(lp->n);
+ cmc_now_playing_erase(&lp->np);
+}
+
+static void render_list_entries(struct cmc_list_pane *lp, struct cmc_list_tab *tab, s32 max_height)
+{
+ struct cmc_list *list = tab->list;
+ struct ncplane *n = lp->n;
+ s32 tracked = tab->move.active ? tab->move.selected + tab->move.offset : tab->selected;
+ s32 count = (s32)list->entries.count;
+ s32 midpoint = max_height / 2;
+ s32 rounded = (max_height + 1) / 2;
+ s32 top, end;
+ if (tracked < midpoint) {
+ top = 0;
+ end = MIN(top + max_height, count);
+ } else if (tracked + rounded >= count) {
+ top = MAX(count - max_height, 0);
+ end = count;
+ } else {
+ top = tracked - midpoint;
+ end = tracked + rounded;
+ }
+ s32 index;
+ struct cmc_list_entry *entry;
+ for (s32 i = top; i < end; i++) {
+ index = i;
+ if (tab->move.active) {
+ if (index == tracked) {
+ index = tab->move.selected;
+ } else {
+ if (index > tracked) index--;
+ if (index >= tab->move.selected) index++;
+ }
+ }
+ entry = al_array_at(list->entries, index);
+ bool on_current = list->current >= 0 && index == list->current;
+ bool styled = i == tracked;
+ if (styled) {
+ ncplane_set_styles(n, NCSTYLE_BOLD);
+ ncplane_set_fg_palindex(n, 4);
+ }
+ s32 y = i - top;
+ u32 length = entry->name.length;
+ if (tab->move.active && i == tracked) {
+ cmc_ui_putnwstr_yx(n, y, 0, length, &entry->name);
+ } else if (on_current) {
+ ncplane_putchar_yx(n, y, 1, '>');
+ cmc_ui_putnwstr_yx(n, y, 3, length, &entry->name);
+ } else {
+ cmc_ui_putnwstr_yx(n, y, 1, length, &entry->name);
+ }
+ if (styled) {
+ ncplane_set_styles(n, NCSTYLE_NONE);
+ ncplane_set_fg_default(n);
+ }
+ }
+}
+
+void cmc_list_pane_render(struct cmc_list_pane *lp)
+{
+ if (lp->selected == -1) return;
+ struct cmc_list_tab *tab = &al_array_at(lp->tabs, lp->selected);
+ render_list_entries(lp, tab, lp->page_length);
+ cmc_now_playing_render(&lp->np, tab_current_entry(tab));
+}
+
+//void cmc_lp_init(struct cmc_ui *ui)
+//{
+// al_array_init(ui->lp.tabs);
+// ui->lp.current = 0;
+//}
+//
+//void cmc_lp_layout(struct cmc_ui *ui, struct ncplane *parent)
+//{
+// if (ui->lp.n) ncplane_destroy(ui->lp.n);
+// struct ncplane_options nopts = { 0 };
+// nopts.rows = ncplane_dim_y(parent);
+// nopts.cols = ncplane_dim_x(parent);
+// ui->lp.n = ncplane_create(parent, &nopts);
+//}
+//
+//void cmc_lp_add_list(struct cmc_ui *ui, struct cmc_list *list)
+//{
+// struct cmc_list_tab tab = {
+// .list = list,
+// .selected = list->current,
+// .leader_pressed = false
+// };
+// al_array_push(ui->lp.tabs, tab);
+//}
+//
+//bool cmc_lp_handle_input(struct cmc_ui *ui, struct ncinput *input)
+//{
+// if (!ui->lp.tabs.count) return false;
+//
+// struct cmc_list_tab *tab = &al_array_at(ui->lp.tabs, ui->lp.current);
+// s32 count = (s32)tab->list->entries.count;
+// bool leader = tab->leader_pressed;
+// if (cmc_ui_consider_input(input, false)) {
+// tab->leader_pressed = false;
+// }
+//
+// switch (input->id) {
+// case 'g':
+// CONSIDER_INPUT();
+// if (leader) {
+// if (tab->move.active) {
+// tab->move.offset = -tab->move.selected;
+// } else {
+// tab->selected = 0;
+// }
+// } else {
+// tab->leader_pressed = true;
+// }
+// break;
+// case 'G':
+// CONSIDER_INPUT();
+// if (tab->move.active) {
+// tab->move.offset = count - 1 - tab->move.selected;
+// } else {
+// tab->selected = count - 1;
+// }
+// break;
+// case 'f':
+// //CONSIDER_INPUT_MOD_CTRL();
+// CONSIDER_INPUT_REPEAT();
+// if (tab->move.active) {
+// if (tab->selected + tab->move.offset < count - tab->page_length) {
+// tab->move.offset += tab->page_length;
+// } else {
+// tab->move.offset = count - 1 - tab->move.selected;
+// }
+// } else {
+// if (tab->selected < count - tab->page_length) {
+// tab->selected += tab->page_length;
+// } else {
+// tab->selected = count - 1;
+// }
+// }
+// break;
+// case 'b':
+// //CONSIDER_INPUT_MOD_CTRL();
+// CONSIDER_INPUT_REPEAT();
+// if (tab->move.active) {
+// if (tab->selected + tab->move.offset > tab->page_length) {
+// tab->move.offset -= tab->page_length;
+// } else {
+// tab->move.offset = -tab->selected;
+// }
+// } else {
+// if (tab->selected > tab->page_length) {
+// tab->selected -= tab->page_length;
+// } else {
+// tab->selected = 0;
+// }
+// }
+// break;
+// case 'j':
+// CONSIDER_INPUT_REPEAT();
+// if (tab->move.active) {
+// if (tab->selected + tab->move.offset < count - 1) {
+// tab->move.offset++;
+// }
+// } else {
+// if (tab->selected < count - 1) {
+// tab->selected++;
+// }
+// }
+// break;
+// case 'k':
+// CONSIDER_INPUT_REPEAT();
+// if (tab->move.active) {
+// if (tab->selected + tab->move.offset > 0) {
+// tab->move.offset--;
+// }
+// } else {
+// if (tab->selected > 0) {
+// tab->selected--;
+// }
+// }
+// break;
+// case 'i':
+// CONSIDER_INPUT();
+// if (!tab->move.active) {
+// tab->move.active = true;
+// tab->move.selected = tab->selected;
+// tab->move.offset = 0;
+// }
+// break;
+// case NCKEY_ESC:
+// CONSIDER_INPUT();
+// if (tab->move.active) {
+// tab->move.active = false;
+// }
+// break;
+// case NCKEY_RETURN:
+// CONSIDER_INPUT();
+// if (tab->move.active) {
+// tab->move.active = false;
+// } else if (tab->selected != tab->list->current) {
+// camu_client_skipto(&ui->c->client, &tab->list->name, tab->selected);
+// }
+// break;
+// case NCKEY_BUTTON1:
+// CONSIDER_INPUT();
+// if (input->x < 0 || input->y < 0) return false;
+// u32 x = (u32)input->x;
+// u32 y = (u32)input->y;
+// ui->last_mouse_x = x;
+// ui->last_mouse_y = y;
+// u64 now = nn_get_timestamp();
+// u64 last = ui->last_mouse_ts;
+// if (last != (u64)-1 && now - last < 100000) {
+// return false;
+// }
+// ui->last_mouse_ts = now;
+// u32 width = ncplane_dim_x(ui->lp.n);
+// u32 height = ncplane_dim_y(ui->lp.n);
+// if (x >= 2 && x <= width && y > height - 3) { // @TODO: Make this defined, more to nowplaying.
+// struct cmc_list_entry *entry = al_array_at(tab->list->entries, tab->list->current);
+// u64 pos = ((x - 2) / (f64)(width - 2)) * entry->duration;
+// camu_client_seek(&ui->c->client, &tab->list->name, entry->id, pos);
+// }
+// break;
+// }
+//
+// return true;
+//}
+//
+//void cmc_lp_erase(struct cmc_ui *ui)
+//{
+// ncplane_erase(ui->lp.n);
+//}
+//
+//static void render_list_entries(struct cmc_ui *ui, struct cmc_list_tab *tab, s32 max_height)
+//{
+// struct cmc_list *list = tab->list;
+// struct ncplane *n = ui->lp.n;
+// s32 tracked = tab->move.active ? tab->move.selected + tab->move.offset : tab->selected;
+// s32 count = (s32)list->entries.count;
+// s32 midpoint = max_height / 2;
+// s32 rounded = (max_height + 1) / 2;
+// s32 top, end;
+// if (tracked < midpoint) {
+// top = 0;
+// end = MIN(top + max_height, count);
+// } else if (tracked + rounded >= count) {
+// top = count - max_height;
+// end = count;
+// } else {
+// top = tracked - midpoint;
+// end = tracked + rounded;
+// }
+// s32 index;
+// struct cmc_list_entry *entry;
+// for (s32 i = top; i < end; i++) {
+// index = i;
+// if (tab->move.active) {
+// if (index == tracked) {
+// index = tab->move.selected;
+// } else {
+// if (index > tracked) index--;
+// if (index >= tab->move.selected) index++;
+// }
+// }
+// entry = al_array_at(list->entries, index);
+// bool on_current = list->current >= 0 && index == list->current;
+// bool styled = i == tracked;
+// if (styled) {
+// ncplane_set_styles(n, NCSTYLE_BOLD);
+// ncplane_set_fg_palindex(n, 4);
+// }
+// s32 y = i - top;
+// u32 length = entry->name.length;
+// if (tab->move.active && i == tracked) {
+// cmc_ui_putnwstr_yx(n, y, 0, length, &entry->name);
+// } else if (on_current) {
+// ncplane_putchar_yx(n, y, 1, '>');
+// cmc_ui_putnwstr_yx(n, y, 3, length, &entry->name);
+// } else {
+// cmc_ui_putnwstr_yx(n, y, 1, length, &entry->name);
+// }
+// if (styled) {
+// ncplane_set_styles(n, NCSTYLE_NONE);
+// ncplane_set_fg_default(n);
+// }
+// }
+//}
+//
+//static void render_now_playing(struct cmc_ui *ui, struct cmc_list_tab *tab, u32 y, u32 height)
+//{
+// struct ncplane *n = ui->lp.n;
+// u32 width = ncplane_dim_x(n);
+//
+// struct cmc_list_entry *entry = NULL;
+// if (tab->list->entries.count > (u32)tab->list->current) {
+// entry = al_array_at(tab->list->entries, tab->list->current);
+// }
+//
+// u64 pos = 0;
+// u64 duration = 0;
+// u64 remaining = 0;
+// u32 y_off = height - 7;
+//
+// ncplane_set_styles(n, NCSTYLE_BOLD);
+// if (entry) {
+// pos = entry->offset;
+// duration = entry->duration;
+// u64 now = nn_get_timestamp();
+// bool paused = entry->start == LIANA_TIMESTAMP_INVALID;
+// if (!paused && now > entry->start) {
+// pos += now - entry->start;
+// }
+// pos = MIN(pos, duration);
+// remaining = duration - pos;
+// //ncplane_putstr_yx(n, y_off, 2, "▸");
+// //ncplane_putstr_yx(n, y_off, 2, "▶");
+// //ncplane_putstr_yx(n, y_off, 2, "⏵ ");
+// //ncplane_putstr_yx(n, y_off, 2, "|");
+// //ncplane_putstr_yx(n, y_off, 2, "|");
+// //ncplane_putstr_yx(n, y_off, 2, "⏵");
+// //ncplane_putstr_yx(n, y_off, 2, "⏸");
+// if (remaining == 0) {
+// ncplane_putstr_yx(n, y_off - 1, 2, "∨");
+// } else {
+// ncplane_putstr_yx(n, y_off - 1, 2, paused ? "^": ">");
+// }
+// } else {
+// ncplane_putstr_yx(n, y_off - 1, 2, "∨");
+// }
+// ncplane_set_styles(n, NCSTYLE_NONE);
+//
+// f32 percent = 1.f;
+// if (duration > 0) percent = MIN(pos / (f32)duration, 1.f);
+// for (u32 i = 0; i < (width - 2) * percent; i++) {
+// ncplane_putchar_yx(n, y_off, i + 1, '-');
+// }
+//
+// y_off--;
+//
+// char timestr[16] = { 0 }; // max = 829128280:01:49\0
+// s32 offset = camu_print_time(timestr, sizeof(timestr), pos, true, 4);
+// ncplane_putstr_yx(n, y_off, 4, timestr);
+// ncplane_putstr_yx(n, y_off, 4 + offset, "⎹");
+//
+// if (entry) {
+// cmc_ui_putnwstr_yx(n, y_off, 6 + offset, MIN(entry->name.length, (u32)60), &entry->name);
+// }
+//
+// bool show_remaining = false;
+// if (show_remaining) {
+// offset = camu_print_time(timestr, sizeof(timestr), remaining, true, 4);
+// } else {
+// offset = camu_print_time(timestr, sizeof(timestr), duration, true, 4);
+// }
+// ncplane_putstr_yx(n, y_off, width - 4 - offset, "⎸");
+// // "=" Duration, "+" Segment, "-" Remaining.
+// if (show_remaining) {
+// ncplane_putstr_yx(n, y_off, width - 3 - offset, "-");
+// } else {
+// ncplane_putstr_yx(n, y_off, width - 3 - offset, "=");
+// }
+// ncplane_putstr_yx(n, y_off, width - 2 - offset, timestr);
+//
+// /*
+// ncplane_putchar_yx(n, height - 5, 2, 'A');
+// ncplane_putchar_yx(n, height - 4, 2, 'A');
+// ncplane_putchar_yx(n, height - 3, 2, 'A');
+// ncplane_putchar_yx(n, height - 2, 2, 'A');
+// */
+//
+// if (entry && entry->raw.data != NULL) {
+// u32 length = width - 2;
+// u32 top = height - 6;
+// u32 wave_height = 5;
+// if (entry->raw.drawing == NULL || entry != ui->lp.blitted) {
+// entry->raw.drawing = al_malloc(sizeof(char **) * wave_height);
+// for (u32 j = 0; j < wave_height; j++) {
+// entry->raw.drawing[j] = al_malloc(sizeof(char *) * length);
+// for (u32 i = 0; i < length; i++) {
+// entry->raw.drawing[j][i] = al_calloc(1, 6);
+// }
+// }
+// if (!ui->lp.w) {
+// struct ncplane_options opts = { 0 };
+// opts.x = 1;
+// opts.y = top;
+// opts.cols = length;
+// opts.rows = wave_height;
+// ui->lp.w = ncplane_create(n, &opts);
+// }
+// /*
+// struct nccell ncl;
+// nccell_init(&ncl);
+// nccell_load(w, &ncl, " ");
+// nccell_set_bg_palindex(&ncl, 3);
+// ncplane_polyfill_yx(w, 0, 0, &ncl);
+// */
+// u32 *rgba;
+// u32 pixel_width, pixel_height;
+// ncplane_pixel_geom(ui->lp.w, &pixel_height, &pixel_width, NULL, NULL, NULL, NULL);
+// //cmc_draw_waveform(entry, length, wave_height, false, entry->raw.drawing, &rgba);
+// cmc_draw_waveform(entry, pixel_width, pixel_height, false, entry->raw.drawing, &rgba);
+// if (ui->lp.vis) {
+// ncvisual_destroy(ui->lp.vis);
+// }
+// ui->lp.vis = ncvisual_from_rgba(rgba, pixel_height, pixel_width * 4, pixel_width);
+// }
+//
+// if (entry != ui->lp.blitted) {
+// ncplane_erase(ui->lp.w);
+// struct ncvisual_options vopts = { 0 };
+// vopts.blitter = NCBLIT_PIXEL;
+// vopts.scaling = NCSCALE_STRETCH;
+// vopts.flags = NCVISUAL_OPTION_NOINTERPOLATE;
+// vopts.n = ui->lp.w;
+// vopts.x = 0;
+// vopts.y = 0;
+// ncvisual_blit(ui->nc, ui->lp.vis, &vopts);
+// ui->lp.blitted = entry;
+// }
+//
+// /*
+// ncplane_set_fg_palindex(n, 5);
+// for (u32 j = 0; j < wave_height; j++) {
+// for (u32 i = 0; i < length; i++) {
+// char *cell = entry->raw.drawing[j][i];
+// if (cell[0] == 'R') {
+// ncplane_set_bg_palindex(n, 2);
+// }
+// ncplane_putstr_yx(n, top + j, 1 + i, cell + 1);
+// if (cell[0] == 'R') {
+// ncplane_set_bg_default(n);
+// }
+// }
+// }
+// ncplane_set_fg_default(n);
+// */
+// }
+//
+// /*
+// char buf[24];
+// al_snprintf(buf, sizeof(buf), "%u, %u | %u, %u", ui->last_mouse_x, ui->last_mouse_y, width, height);
+// ncplane_putstr_yx(n, height - 5, 5, buf);
+// */
+//
+// u64 c = 0;
+// ncchannels_set_fg_default(&c);
+// ncplane_cursor_move_yx(n, y, 0);
+// ncplane_light_box(n, NCSTYLE_NONE, c, height - 1, width - 1, 0);
+//}
+//
+//void cmc_lp_render(struct cmc_ui *ui)
+//{
+// u32 height = ncplane_dim_y(ui->lp.n);
+//
+// if (ui->lp.tabs.count <= ui->lp.current) return;
+// struct cmc_list_tab *tab = &al_array_at(ui->lp.tabs, ui->lp.current);
+//
+// tab->page_length = height - 9;
+// render_list_entries(ui, tab, tab->page_length);
+// render_now_playing(ui, tab, tab->page_length, height);
+//}
diff --git a/src/fruits/cmc/ui/panes/list.h b/src/fruits/cmc/ui/panes/list.h
new file mode 100644
index 0000000..bfeed59
--- /dev/null
+++ b/src/fruits/cmc/ui/panes/list.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <al/array.h>
+#include <notcurses/notcurses.h>
+
+#include "../widgets/now_playing.h"
+
+struct cmc_list;
+struct cmc_list_tab {
+ struct cmc_list *list;
+ s32 selected;
+ bool leader_pressed;
+ struct {
+ bool active;
+ s32 selected;
+ s32 offset;
+ } move;
+};
+
+struct cmc_list_pane {
+ struct ncplane *n;
+ array(struct cmc_list_tab) tabs;
+ s32 selected;
+ s32 page_length;
+ struct cmc_now_playing np;
+ struct cmc_ui *ui;
+};
+
+void cmc_list_pane_init(struct cmc_list_pane *lp, struct cmc_ui *ui);
+bool cmc_list_pane_layout(struct cmc_list_pane *lp, struct ncplane *parent);
+void cmc_list_pane_add_list(struct cmc_list_pane *lp, struct cmc_list *list);
+bool cmc_list_pane_handle_input(struct cmc_list_pane *lp, struct ncinput *input);
+bool cmc_list_pane_tick(struct cmc_list_pane *lp);
+void cmc_list_pane_erase(struct cmc_list_pane *lp);
+void cmc_list_pane_render(struct cmc_list_pane *lp);
diff --git a/src/fruits/cmc/ui/panes/log.c b/src/fruits/cmc/ui/panes/log.c
new file mode 100644
index 0000000..be9702c
--- /dev/null
+++ b/src/fruits/cmc/ui/panes/log.c
@@ -0,0 +1,65 @@
+#include "log.h"
+
+void cmc_log_pane_init(struct cmc_log_pane *lp)
+{
+ lp->new_message = false;
+ al_array_init(lp->messages);
+}
+
+bool cmc_log_pane_layout(struct cmc_log_pane *lp, struct ncplane *parent)
+{
+ if (lp->n) ncplane_destroy(lp->n);
+ struct ncplane_options nopts = { 0 };
+ nopts.rows = ncplane_dim_y(parent);
+ nopts.cols = ncplane_dim_x(parent);
+ lp->n = ncplane_create(parent, &nopts);
+ return true;
+}
+
+void cmc_log_pane_push_message(struct cmc_log_pane *lp, char *message)
+{
+ al_array_push(lp->messages, message);
+ lp->new_message = true;
+}
+
+bool cmc_log_pane_handle_input(struct cmc_log_pane *lp, struct ncinput *input)
+{
+ (void)lp;
+ (void)input;
+ return false;
+}
+
+bool cmc_log_pane_tick(struct cmc_log_pane *lp)
+{
+ if (lp->new_message) {
+ lp->new_message = false;
+ return true;
+ }
+ return false;
+}
+
+void cmc_log_pane_erase(struct cmc_log_pane *lp)
+{
+ ncplane_erase(lp->n);
+}
+
+void cmc_log_pane_render(struct cmc_log_pane *lp)
+{
+ struct ncplane *n = lp->n;
+
+ u32 max_height = ncplane_dim_y(n);
+
+ u32 count = lp->messages.count;
+ u32 index = (count > max_height) ? count - max_height : 0;
+ for (u32 i = index; i < count; i++) {
+ // We can't use putnstr to control the width because these messages will have wide characters.
+ ncplane_putstr_yx(n, i - index, 0, al_array_at(lp->messages, i));
+ }
+
+ if (index > 0) {
+ //for (u32 i = 0; i < index; i++) {
+ // al_free(al_array_at(lp->messages, i));
+ //}
+ //al_array_remove_range(lp->messages, 0, index);
+ }
+}
diff --git a/src/fruits/cmc/ui/panes/log.h b/src/fruits/cmc/ui/panes/log.h
new file mode 100644
index 0000000..d79c7e9
--- /dev/null
+++ b/src/fruits/cmc/ui/panes/log.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <al/array.h>
+#include <notcurses/notcurses.h>
+
+struct cmc_log_pane {
+ struct ncplane *n;
+ bool new_message;
+ array(char *) messages;
+};
+
+void cmc_log_pane_init(struct cmc_log_pane *lp);
+bool cmc_log_pane_layout(struct cmc_log_pane *lp, struct ncplane *parent);
+void cmc_log_pane_push_message(struct cmc_log_pane *lp, char *message);
+bool cmc_log_pane_handle_input(struct cmc_log_pane *lp, struct ncinput *input);
+bool cmc_log_pane_tick(struct cmc_log_pane *lp);
+void cmc_log_pane_erase(struct cmc_log_pane *lp);
+void cmc_log_pane_render(struct cmc_log_pane *lp);
diff --git a/src/fruits/cmc/ui/panes/search.c b/src/fruits/cmc/ui/panes/search.c
new file mode 100644
index 0000000..e46c27d
--- /dev/null
+++ b/src/fruits/cmc/ui/panes/search.c
@@ -0,0 +1,226 @@
+#include "../../cmc.h"
+
+#include "../ui.h"
+
+AL_ASSERT_TYPE_SIZE(wchar_t, 4);
+
+//void cmc_sp_init(struct cmc_ui *ui)
+//{
+// al_array_init(ui->sp.tabs);
+// ui->sp.current = 0;
+//}
+//
+//void cmc_sp_layout(struct cmc_ui *ui, struct ncplane *parent)
+//{
+// if (ui->sp.n) ncplane_destroy(ui->sp.n);
+// struct ncplane_options nopts = { 0 };
+// nopts.rows = ncplane_dim_y(parent) - 1;
+// nopts.cols = ncplane_dim_x(parent) - 1;
+// ui->sp.n = ncplane_create(parent, &nopts);
+// struct cmc_search_tab tab = {
+// .search = NULL,
+// };
+// nopts.x = 3;
+// nopts.y = 2;
+// nopts.rows = 10;
+// nopts.cols = ncplane_dim_x(ui->sp.n) - 3;
+// tab.input = ncplane_create(ui->sp.n, &nopts);
+// tab.cursor = 0;
+// tab.input_active = false;
+// tab.input_text = al_wstr_null();
+// tab.input_changed = false;
+// al_array_push(ui->sp.tabs, tab);
+//}
+//
+//void cmc_sp_add_search(struct cmc_ui *ui, struct cmc_search *search)
+//{
+// struct cmc_search_tab tab = {
+// .search = search,
+// };
+// struct ncplane_options nopts = { 0 };
+// nopts.x = 3;
+// nopts.y = 2;
+// nopts.rows = 10;
+// nopts.cols = ncplane_dim_x(ui->sp.n) - 3;
+// tab.input = ncplane_create(ui->sp.n, &nopts);
+// tab.cursor = 0;
+// tab.input_active = false;
+// tab.input_text = al_wstr_null();
+// tab.input_changed = false;
+// al_array_push(ui->sp.tabs, tab);
+//}
+//
+//static bool handle_text_input(struct cmc_search_tab *tab, struct ncinput *input)
+//{
+// bool entered = false;
+//
+// switch (input->id) {
+// case NCKEY_BACKSPACE:
+// if (tab->cursor > 0) {
+// al_wstr_remove_at(&tab->input_text, tab->cursor - 1);
+// tab->cursor--;
+// tab->input_changed = true;
+// }
+// break;
+// case NCKEY_LEFT:
+// if (tab->cursor > 0) {
+// tab->cursor--;
+// }
+// break;
+// case NCKEY_RIGHT:
+// if (tab->cursor < tab->input_text.length) {
+// tab->cursor++;
+// }
+// break;
+// case NCKEY_RETURN:
+// entered = true;
+// // fallthrough
+// case NCKEY_ESC:
+// tab->input_active = false;
+// tab->input_changed = true; // Erase text on next render.
+// break;
+// default:
+// if (ncinput_ctrl_p(input)) {
+// switch (input->id) {
+// case 'A':
+// tab->cursor = 0;
+// break;
+// case 'E':
+// tab->cursor = tab->input_text.length;
+// break;
+// case 'B':
+// if (tab->cursor > 0) {
+// tab->cursor--;
+// }
+// break;
+// case 'F':
+// if (tab->cursor < tab->input_text.length) {
+// tab->cursor++;
+// }
+// break;
+// case 'U':
+// for (u32 i = 1; i <= tab->cursor; i++) {
+// al_wstr_remove_at(&tab->input_text, tab->cursor - i);
+// }
+// tab->cursor = 0;
+// tab->input_changed = true;
+// break;
+// }
+// break;
+// }
+//
+// if (ncinput_alt_p(input) || nckey_synthesized_p(input->id)) {
+// break;
+// }
+//
+// al_wstr_insert(&tab->input_text, tab->cursor, &al_wstr_w((wchar_t *)input->eff_text, 0, 1));
+// tab->cursor++;
+// tab->input_changed = true;
+//
+// /*
+// // https://github.com/dankamongmen/notcurses/blob/c11efe877f2245901f5c9ce110de7a2c83cfa2ed/src/lib/reader.c#L404
+// for (s32 c = 0; input->eff_text[c] != 0; c++){
+// uchar egc[5] = { 0 };
+// if (notcurses_ucs32_to_utf8(&input->eff_text[c], 1, egc, 4) >= 0) {
+// // This probably doesn't work on Windows.
+// al_wstr_insert(&tab->input_text, tab->cursor, &al_wstr_w((wchar_t *)egc, 0, 1));
+// }
+// }
+// */
+//
+// break;
+// }
+//
+// tab->visual_cursor = 0;
+// if (tab->cursor > 0) {
+// for (u32 i = 0; i < tab->cursor; i++) {
+// tab->visual_cursor += al_wchar_width(al_wstr_at(&tab->input_text, i));
+// }
+// }
+//
+// return entered;
+//}
+//
+//bool cmc_sp_handle_input(struct cmc_ui *ui, struct ncinput *input)
+//{
+// struct cmc_search_tab *tab = NULL;
+// if (ui->sp.tabs.count > 0) {
+// tab = &al_array_at(ui->sp.tabs, ui->sp.current);
+// }
+//
+// if (tab) {
+// if (tab->input_active) {
+// if (cmc_ui_consider_input(input, true)) {
+// if (handle_text_input(tab, input)) {
+// str query;
+// al_str_from_wstr(&query, &tab->input_text);
+// camu_client_create_search(&ui->c->client, &al_str_c("youtube"), &query);
+// al_str_free(&query);
+// }
+// if (!tab->input_active) notcurses_cursor_disable(ui->nc);
+// }
+// } else {
+// switch (input->id) {
+// case 'i':
+// CONSIDER_INPUT();
+// tab->input_active = true;
+// tab->input_changed = true;
+// s32 absx = ncplane_abs_x(tab->input);
+// s32 absy = ncplane_abs_y(tab->input);
+// notcurses_cursor_enable(ui->nc, absy, absx + tab->visual_cursor);
+// break;
+// }
+// }
+// }
+//
+// return true;
+//}
+//
+//void cmc_sp_erase(struct cmc_ui *ui)
+//{
+// ncplane_erase(ui->sp.n);
+//}
+//
+//void cmc_sp_render(struct cmc_ui *ui)
+//{
+// struct cmc_search_tab *tab = NULL;
+// if (ui->sp.tabs.count > 0) {
+// tab = &al_array_at(ui->sp.tabs, ui->sp.current);
+// }
+//
+// if (tab) {
+// if (tab->input_changed) {
+// ncplane_erase(tab->input);
+// if (tab->input_active) {
+// cmc_ui_putnwstr_yx(tab->input, 0, 0, tab->input_text.length, &tab->input_text);
+// }
+// tab->input_changed = false;
+// }
+// if (tab->input_active) {
+// s32 absx = ncplane_abs_x(tab->input);
+// s32 absy = ncplane_abs_y(tab->input);
+// notcurses_cursor_enable(ui->nc, absy, absx + tab->visual_cursor);
+// }
+// }
+//
+// /*
+// if (ui->overlay_shown) {
+// ncplane_erase(ui->oln);
+// u32 max_height = ncplane_dim_y(ui->oln) - 2;
+// u32 count = ui->c->searches.count;
+// u32 index = ui->sp.overlay_index - (ui->sp.overlay_index % max_height);
+// u32 end = MIN(index + max_height, count);
+// for (u32 i = index; i < end; i++) {
+// struct cmc_search *search = al_array_at(ui->c->searches, i);
+// if (i == ui->sp.overlay_index) {
+// ncplane_set_fg_palindex(ui->oln, 6);
+// ncplane_putchar_yx(ui->oln, i - index + 1, 2, '>');
+// cmc_ui_putstr_maxwidth_yx(ui->oln, i - index + 1, 4, search->query.length, &search->query);
+// } else {
+// ncplane_set_fg_default(ui->oln);
+// cmc_ui_putstr_maxwidth_yx(ui->oln, i - index + 1, 2, search->query.length, &search->query);
+// }
+// }
+// }
+// */
+//}
diff --git a/src/fruits/cmc/ui/ui.c b/src/fruits/cmc/ui/ui.c
index 88c9c5d..7a0d8a1 100644
--- a/src/fruits/cmc/ui/ui.c
+++ b/src/fruits/cmc/ui/ui.c
@@ -1,8 +1,9 @@
#include <al/log.h>
#include <al/lib.h>
+#include "util/notcurses_ext.h"
+
#include "ui.h"
-#include "ext.h"
void cmc_ui_putnwstr_yx(struct ncplane *n, u32 y, u32 x, u32 width, wstr *w)
{
@@ -36,70 +37,54 @@ static bool read_input(struct cmc_ui *ui, struct ncinput *input)
static void erase_previous_pane(struct cmc_ui *ui, u8 previous, u8 upcoming)
{
- struct ncplane *pn = NULL;
switch (previous) {
case CMC_PANE_LIST:
- pn = ui->lp.n;
- break;
- case CMC_PANE_SEARCH:
- pn = ui->sp.n;
+ cmc_list_pane_erase(&ui->lists);
break;
case CMC_PANE_LOG:
- pn = ui->logp.n;
+ cmc_log_pane_erase(&ui->log);
break;
}
- ncplane_erase(pn);
switch (upcoming) {
case CMC_PANE_LIST:
- ncplane_move_top(ui->lp.n);
- //ncplane_move_family_below(pn, ui->lp.n);
- break;
- case CMC_PANE_SEARCH:
- ncplane_move_top(ui->sp.n);
- //ncplane_move_family_below(pn, ui->sp.n);
+ ncplane_move_family_top(ui->lists.n);
break;
case CMC_PANE_LOG:
- ncplane_move_top(ui->logp.n);
- //ncplane_move_family_below(pn, ui->logp.n);
+ ncplane_move_family_top(ui->log.n);
break;
}
}
-static void switch_to_pane(struct cmc_ui *ui, u8 pane)
+static bool switch_to_pane(struct cmc_ui *ui, u8 pane)
{
- erase_previous_pane(ui, ui->pane, pane);
- ui->pane = pane;
+ if (pane != ui->pane) {
+ erase_previous_pane(ui, ui->pane, pane);
+ ui->pane = pane;
+ return true;
+ }
+ return false;
}
void cmc_ui_queue_render(struct cmc_ui *ui)
{
- ncplane_erase(ui->n);
+ //ncplane_erase(ui->n);
switch (ui->pane) {
case CMC_PANE_LIST:
- cmc_lp_erase(ui);
- break;
- case CMC_PANE_SEARCH:
- cmc_sp_erase(ui);
+ cmc_list_pane_erase(&ui->lists);
break;
case CMC_PANE_LOG:
- cmc_logp_erase(ui);
- break;
+ cmc_log_pane_erase(&ui->log);
}
- if (ui->overlay_shown) {
- ncplane_erase(ui->oln);
- }
+ if (ui->overlay_shown) ncplane_erase(ui->oln);
switch (ui->pane) {
case CMC_PANE_LIST:
- cmc_lp_render(ui);
- break;
- case CMC_PANE_SEARCH:
- cmc_sp_render(ui);
+ cmc_list_pane_render(&ui->lists);
break;
case CMC_PANE_LOG:
- cmc_logp_render(ui);
+ cmc_log_pane_render(&ui->log);
break;
}
@@ -112,11 +97,11 @@ void cmc_ui_queue_render(struct cmc_ui *ui)
nccell_set_fg_palindex(&ncl, 3);
ncplane_polyfill_yx(ui->oln, 0, 0, &ncl);
*/
- u32 width = ncplane_dim_x(ui->oln);
- u32 height = ncplane_dim_y(ui->oln);
u64 c = 0;
ncchannels_set_fg_default(&c);
ncplane_cursor_move_yx(ui->oln, 0, 0);
+ u32 width = ncplane_dim_x(ui->oln);
+ u32 height = ncplane_dim_y(ui->oln);
ncplane_light_box(ui->oln, NCSTYLE_NONE, c, height - 1, width - 1, 0);
}
@@ -126,8 +111,11 @@ void cmc_ui_queue_render(struct cmc_ui *ui)
void cmc_ui_toggle_overlay(struct cmc_ui *ui)
{
ui->overlay_shown = !ui->overlay_shown;
- if (!ui->overlay_shown) {
+ if (ui->overlay_shown) {
+ ncplane_move_top(ui->oln);
+ } else {
ncplane_erase(ui->oln);
+ ncplane_move_bottom(ui->oln);
}
}
@@ -145,15 +133,15 @@ static void input_poll_callback(void *userdata, s32 revents)
nn_event_loop_break_one(ui->loop);
break;
case '1':
- switch_to_pane(ui, CMC_PANE_LIST);
+ do_render |= switch_to_pane(ui, CMC_PANE_LIST);
break;
case '2':
break;
case '3':
- switch_to_pane(ui, CMC_PANE_SEARCH);
+ do_render |= switch_to_pane(ui, CMC_PANE_SEARCH);
break;
case '4':
- switch_to_pane(ui, CMC_PANE_LOG);
+ do_render |= switch_to_pane(ui, CMC_PANE_LOG);
break;
case NCKEY_TAB:
cmc_ui_toggle_overlay(ui);
@@ -162,22 +150,14 @@ static void input_poll_callback(void *userdata, s32 revents)
}
switch (ui->pane) {
case CMC_PANE_LIST:
- if (cmc_lp_handle_input(ui, &input)) {
- do_render |= true;
- }
- break;
- case CMC_PANE_SEARCH:
- if (cmc_sp_handle_input(ui, &input)) {
- do_render |= true;
- }
+ do_render |= cmc_list_pane_handle_input(&ui->lists, &input);
break;
case CMC_PANE_LOG:
- do_render = true;
+ do_render |= cmc_log_pane_handle_input(&ui->log, &input);
break;
}
if (do_render) {
cmc_ui_queue_render(ui);
- notcurses_render(ui->nc);
}
}
}
@@ -186,16 +166,29 @@ static void render_timer_callback(void *userdata, struct nn_timer *timer)
{
struct cmc_ui *ui = (struct cmc_ui *)userdata;
(void)timer;
- cmc_ui_queue_render(ui);
- notcurses_render(ui->nc);
+ bool do_render = false;
+ switch (ui->pane) {
+ case CMC_PANE_LIST:
+ do_render |= cmc_list_pane_tick(&ui->lists);
+ break;
+ case CMC_PANE_LOG:
+ do_render |= cmc_log_pane_tick(&ui->log);
+ break;
+ }
+ if (do_render) {
+ cmc_ui_queue_render(ui);
+ }
nn_timer_again(&ui->render_timer);
}
-static bool relayout(struct cmc_ui *ui)
+static bool relayout(struct cmc_ui *ui, struct ncplane *stdplane)
{
- struct ncplane *stdplane = notcurses_stdplane(ui->nc);
notcurses_stddim_yx(ui->nc, &ui->term_rows, &ui->term_cols);
+ /* TBD
+ if (ui->term_cols < 6 || ui->term_rows < 6) return false;
+ */
+
if (ui->n) ncplane_destroy(ui->n);
struct ncplane_options nopts = { 0 };
nopts.x = 1;
@@ -205,25 +198,41 @@ static bool relayout(struct cmc_ui *ui)
nopts.flags = NCPLANE_OPTION_MARGINALIZED;
ui->n = ncplane_create(stdplane, &nopts);
- cmc_lp_layout(ui, ui->n);
- cmc_sp_layout(ui, ui->n);
- cmc_logp_layout(ui, ui->n);
+ bool layed_out = true;
+ layed_out &= cmc_list_pane_layout(&ui->lists, ui->n);
+ layed_out &= cmc_log_pane_layout(&ui->log, ui->n);
+
+ switch (ui->pane) {
+ case CMC_PANE_LIST:
+ ncplane_move_family_top(ui->lists.n);
+ break;
+ case CMC_PANE_LOG:
+ ncplane_move_family_top(ui->log.n);
+ break;
+ }
if (ui->oln) ncplane_destroy(ui->oln);
- nopts.margin_r = MAX((u32)6, ncplane_dim_x(ui->n) / 2);
- nopts.margin_b = MAX((u32)2, ncplane_dim_y(ui->n) / 2);
- nopts.x = nopts.margin_r / 2;
- nopts.y = nopts.margin_b / 2;
+ nopts.margin_r = 0;
+ nopts.margin_b = 0;
+ nopts.x = NCALIGN_CENTER;
+ nopts.y = NCALIGN_CENTER;
+ nopts.rows = MAX((u32)1, ncplane_dim_y(ui->n) / 2);
+ nopts.cols = MAX((u32)6, ncplane_dim_x(ui->n) / 2);
+ nopts.flags = NCPLANE_OPTION_VERALIGNED | NCPLANE_OPTION_HORALIGNED;
ui->oln = ncplane_create(ui->n, &nopts);
- ncplane_move_below(ui->oln, ui->n);
+ ncplane_move_bottom(ui->oln);
- return true;
+ return layed_out;
}
static s32 resize_cb(struct ncplane *p)
{
struct cmc_ui *ui = (struct cmc_ui *)ncplane_userptr(p);
- relayout(ui);
+ //al_assert(false);
+ ui->layed_out = relayout(ui, notcurses_stdplane(ui->nc));
+ if (ui->layed_out) {
+ cmc_ui_queue_render(ui);
+ }
return 0;
}
@@ -231,7 +240,7 @@ static s32 log_callback(void *userdata, u8 level, char *message)
{
struct cmc_ui *ui = (struct cmc_ui *)userdata;
(void)level;
- cmc_logp_push_message(ui, message);
+ cmc_log_pane_push_message(&ui->log, message);
return al_strnlen(message, AL_LOG_MESSAGE_SIZE);
}
@@ -249,14 +258,9 @@ bool cmc_ui_init(struct cmc_ui *ui, struct nn_event_loop *loop, struct cmc *c)
ui->loop = loop;
- cmc_lp_init(ui);
- cmc_sp_init(ui);
-
- cmc_logp_init(ui);
+ cmc_list_pane_init(&ui->lists, ui);
al_set_print(log_callback, ui);
-
- ui->pending_layout = false;
- ui->pending_render = false;
+ cmc_log_pane_init(&ui->log);
ui->pane = CMC_PANE_SEARCH;
ui->overlay_shown = false;
@@ -265,12 +269,12 @@ bool cmc_ui_init(struct cmc_ui *ui, struct nn_event_loop *loop, struct cmc *c)
ui->last_mouse_y = 0;
ui->last_mouse_ts = (u64)-1;
- relayout(ui);
-
struct ncplane *stdplane = notcurses_stdplane(ui->nc);
ncplane_set_resizecb(stdplane, resize_cb);
ncplane_set_userptr(stdplane, ui);
+ ui->layed_out = relayout(ui, stdplane);
+
nn_poll_init(&ui->input_poll, input_poll_callback, ui);
nn_poll_set(&ui->input_poll, notcurses_inputready_fd(ui->nc), NNWT_POLL_READ);
nn_poll_start(&ui->input_poll, ui->loop);
diff --git a/src/fruits/cmc/ui/ui.h b/src/fruits/cmc/ui/ui.h
index 302d735..7abf105 100644
--- a/src/fruits/cmc/ui/ui.h
+++ b/src/fruits/cmc/ui/ui.h
@@ -7,12 +7,16 @@
#include <nnwt/timer.h>
#include <notcurses/notcurses.h>
+#include "panes/list.h"
+#include "panes/log.h"
+
enum {
CMC_PANE_LIST = 0,
CMC_PANE_SEARCH,
CMC_PANE_LOG
};
+/*
struct cmc_list_tab {
struct cmc_list *list;
s32 page_length;
@@ -34,6 +38,7 @@ struct cmc_search_tab {
wstr input_text;
bool input_changed;
};
+*/
struct cmc;
struct cmc_ui {
@@ -41,15 +46,17 @@ struct cmc_ui {
struct nn_event_loop *loop;
u32 term_cols;
u32 term_rows;
- bool pending_layout;
- bool pending_render;
u8 pane;
+ bool layed_out;
struct ncplane *n; // window.
struct ncplane *oln; // overlay.
bool overlay_shown;
u32 last_mouse_x;
u32 last_mouse_y;
u64 last_mouse_ts;
+ struct cmc_list_pane lists;
+ struct cmc_log_pane log;
+ /*
struct {
struct ncplane *n;
struct ncplane *w;
@@ -67,6 +74,7 @@ struct cmc_ui {
struct ncplane *n;
array(char *) messages;
} logp;
+ */
struct nn_poll input_poll;
struct nn_timer render_timer;
struct cmc *c;
@@ -90,6 +98,7 @@ void cmc_ui_queue_render(struct cmc_ui *ui);
void cmc_ui_toggle_overlay(struct cmc_ui *ui);
void cmc_ui_close(struct cmc_ui *ui);
+/*
void cmc_lp_init(struct cmc_ui *ui);
void cmc_lp_layout(struct cmc_ui *ui, struct ncplane *parent);
void cmc_lp_add_list(struct cmc_ui *ui, struct cmc_list *list);
@@ -110,3 +119,4 @@ void cmc_logp_push_message(struct cmc_ui *ui, char *message);
bool cmc_logp_handle_input(struct cmc_ui *ui, struct ncinput *input);
void cmc_logp_erase(struct cmc_ui *ui);
void cmc_logp_render(struct cmc_ui *ui);
+*/
diff --git a/src/fruits/cmc/ui/ext.h b/src/fruits/cmc/ui/util/notcurses_ext.h
index b09754e..b09754e 100644
--- a/src/fruits/cmc/ui/ext.h
+++ b/src/fruits/cmc/ui/util/notcurses_ext.h
diff --git a/src/fruits/cmc/ui/waveform.c b/src/fruits/cmc/ui/util/waveform.c
index 9bfc296..8d37543 100644
--- a/src/fruits/cmc/ui/waveform.c
+++ b/src/fruits/cmc/ui/util/waveform.c
@@ -131,72 +131,72 @@ void cmc_draw_waveform(struct cmc_list_entry *entry, u32 width, u32 height, bool
return;
#endif
- u32 channels = entry->raw.fmt.channel_count;
- u32 samples_per_column = entry->raw.sample_count / (width * 2);
-
- s32 pixel_height = height * 4;
- s32 wave_min = INT16_MIN;
- s32 wave_max = INT16_MAX;
- s32 step = (wave_max * 2 + (pixel_height - 1)) / pixel_height;
-
- s16 *steps = al_malloc(sizeof(s16) * (pixel_height + 1));
- for (s32 i = 0; i <= (s32)pixel_height; i++) {
- steps[i] = (s16)MAX(wave_max - step * i, wave_min);
- }
- al_assert(steps[pixel_height] == wave_min);
-
- s64 rms = 0;
- s16 min, max;
- s16 low, high;
- s16 *data = (s16 *)entry->raw.data;
- const u32 samples_stride = samples_per_column * channels;
- u16 masks_for_chars[height];
- //u32 slen[height];
- //al_memset(slen, 0, sizeof(u32) * height);
- for (u32 i = 0; i < width; i++) {
- al_memset(masks_for_chars, 0, sizeof(u16) * height);
- for (u32 k = 0; k < 2; k++) {
- u32 f = (i * 2 + k) * samples_stride;
- for (u32 n = 0; n < channels; n++) {
- rms = 0;
- min = wave_max, max = wave_min;
- for (u32 m = 0; m <= samples_stride; m += channels) {
- s16 ss = data[f + m + n];
- rms += ss * ss;
- min = MIN(min, ss);
- max = MAX(max, ss);
- }
- rms /= samples_per_column;
- rms = sqrt(rms);
- for (u32 j = 0; j < height; j++) {
- for (u32 l = 0; l < 4; l++) {
- u32 step = j * 4 + l;
- low = steps[step + 1], high = steps[step];
- if (low < max && high >= min) {
- if (include_rms && WITHIN_RMS(low, high, rms)) {
- masks_for_chars[j] |= RMS_BIT;
- } else {
- masks_for_chars[j] |= 1 << (l * 2 + k);
- }
- }
- }
- }
- }
- }
- for (u32 j = 0; j < height; j++) {
- if (masks_for_chars[j] & RMS_BIT) {
- buf[j][i][0] = 'R';
- masks_for_chars[j] &= ~RMS_BIT;
- }
- u32 len = masks_for_chars[j] == 0 ? 2 : 5;
- al_memcpy(buf[j][i] + 1, octant_lut[masks_for_chars[j]], len);
- //slen[j] += len;
- }
- }
-
- /*
- for (u32 j = 0; j < height; j++) {
- buf[j][slen[j]] = '\0';
- }
- */
+// u32 channels = entry->raw.fmt.channel_count;
+// u32 samples_per_column = entry->raw.sample_count / (width * 2);
+//
+// s32 pixel_height = height * 4;
+// s32 wave_min = INT16_MIN;
+// s32 wave_max = INT16_MAX;
+// s32 step = (wave_max * 2 + (pixel_height - 1)) / pixel_height;
+//
+// s16 *steps = al_malloc(sizeof(s16) * (pixel_height + 1));
+// for (s32 i = 0; i <= (s32)pixel_height; i++) {
+// steps[i] = (s16)MAX(wave_max - step * i, wave_min);
+// }
+// al_assert(steps[pixel_height] == wave_min);
+//
+// s64 rms = 0;
+// s16 min, max;
+// s16 low, high;
+// s16 *data = (s16 *)entry->raw.data;
+// const u32 samples_stride = samples_per_column * channels;
+// u16 masks_for_chars[height];
+// //u32 slen[height];
+// //al_memset(slen, 0, sizeof(u32) * height);
+// for (u32 i = 0; i < width; i++) {
+// al_memset(masks_for_chars, 0, sizeof(u16) * height);
+// for (u32 k = 0; k < 2; k++) {
+// u32 f = (i * 2 + k) * samples_stride;
+// for (u32 n = 0; n < channels; n++) {
+// rms = 0;
+// min = wave_max, max = wave_min;
+// for (u32 m = 0; m <= samples_stride; m += channels) {
+// s16 ss = data[f + m + n];
+// rms += ss * ss;
+// min = MIN(min, ss);
+// max = MAX(max, ss);
+// }
+// rms /= samples_per_column;
+// rms = sqrt(rms);
+// for (u32 j = 0; j < height; j++) {
+// for (u32 l = 0; l < 4; l++) {
+// u32 step = j * 4 + l;
+// low = steps[step + 1], high = steps[step];
+// if (low < max && high >= min) {
+// if (include_rms && WITHIN_RMS(low, high, rms)) {
+// masks_for_chars[j] |= RMS_BIT;
+// } else {
+// masks_for_chars[j] |= 1 << (l * 2 + k);
+// }
+// }
+// }
+// }
+// }
+// }
+// for (u32 j = 0; j < height; j++) {
+// if (masks_for_chars[j] & RMS_BIT) {
+// buf[j][i][0] = 'R';
+// masks_for_chars[j] &= ~RMS_BIT;
+// }
+// u32 len = masks_for_chars[j] == 0 ? 2 : 5;
+// al_memcpy(buf[j][i] + 1, octant_lut[masks_for_chars[j]], len);
+// //slen[j] += len;
+// }
+// }
+//
+// /*
+// for (u32 j = 0; j < height; j++) {
+// buf[j][slen[j]] = '\0';
+// }
+// */
}
diff --git a/src/fruits/cmc/ui/waveform.h b/src/fruits/cmc/ui/util/waveform.h
index 464d589..763f54a 100644
--- a/src/fruits/cmc/ui/waveform.h
+++ b/src/fruits/cmc/ui/util/waveform.h
@@ -88,7 +88,7 @@ AAAAAAAAAAAAAAAA
#include <al/types.h>
-#include "../cmc.h"
+#include "../../cmc.h"
/*
static const char *bar_lut[] = {
diff --git a/src/fruits/cmc/ui/widgets/now_playing.c b/src/fruits/cmc/ui/widgets/now_playing.c
new file mode 100644
index 0000000..d30590c
--- /dev/null
+++ b/src/fruits/cmc/ui/widgets/now_playing.c
@@ -0,0 +1,223 @@
+#include <math.h>
+
+#include "../../../../liana/list.h"
+#include "../../../../sink/util.h"
+
+#include "../../cmc.h"
+
+#include "../util/waveform.h"
+#include "../util/notcurses_ext.h"
+
+#include "now_playing.h"
+
+void cmc_now_playing_init(struct cmc_now_playing *np)
+{
+ al_array_init(np->visuals);
+}
+
+bool cmc_now_playing_layout(struct cmc_now_playing *np, struct ncplane *parent, u32 y)
+{
+ if (np->n) ncplane_destroy(np->n);
+ u32 width = ncplane_dim_x(parent);
+ u32 height = ncplane_dim_y(parent);
+ struct ncplane_options nopts = { 0 };
+ nopts.x = 0;
+ nopts.y = y;
+ nopts.cols = width;
+ nopts.rows = height - y;
+ np->n = ncplane_create(parent, &nopts);
+ if (np->wave) ncplane_destroy(np->wave);
+ nopts.x = 1;
+ nopts.y = 3;
+ nopts.rows = 5;
+ nopts.cols = width - 2;
+ np->wave = ncplane_create(np->n, &nopts);
+ np->blitted = NULL;
+ struct cmc_now_playing_visual *visual;
+ al_array_foreach_ptr(np->visuals, i, visual) {
+ if (visual->visual) ncvisual_destroy(visual->visual);
+ }
+ np->visuals.count = 0;
+ return true;
+}
+
+static void get_entry_duration_and_pos(struct cmc_list_entry *entry, u64 *duration, u64 *pos)
+{
+ *pos = entry->offset;
+ *duration = entry->duration;
+ u64 now = nn_get_timestamp();
+ bool paused = entry->start == LIANA_TIMESTAMP_INVALID;
+ if (!paused && now > entry->start) {
+ *pos += now - entry->start;
+ }
+ *pos = MIN(*pos, *duration);
+}
+
+bool cmc_now_playing_tick(struct cmc_now_playing *np, struct cmc_list_entry *entry)
+{
+ return true;
+ u64 duration = 0, pos = 0;
+ if (entry) get_entry_duration_and_pos(entry, &duration, &pos);
+ u32 width = ncplane_dim_x(np->n);
+ f32 percent = 1.f;
+ if (duration > 0) percent = MIN(pos / (f32)duration, 1.f);
+ u32 segments = (width - 2) * percent;
+ if (segments != np->segment_count) {
+ np->segment_count = segments;
+ return true;
+ } else {
+ // @TODO: Avoiding judder: Implement and utilize tick timing change.
+ if (pos < np->last_pos || pos - np->last_pos > 1000000) {
+ np->last_pos = pos;
+ return true;
+ }
+ }
+ return false;
+}
+
+void cmc_now_playing_erase(struct cmc_now_playing *np)
+{
+ ncplane_erase(np->n);
+}
+
+void cmc_now_playing_render(struct cmc_now_playing *np, struct cmc_list_entry *entry)
+{
+ u64 duration = 0, pos = 0;
+ if (entry) get_entry_duration_and_pos(entry, &duration, &pos);
+ u64 remaining = duration - pos;
+
+ ncplane_set_styles(np->n, NCSTYLE_BOLD);
+ if (remaining == 0) {
+ ncplane_putstr_yx(np->n, 1, 2, "∨");
+ } else {
+ al_assert(entry);
+ if (remaining == 0) {
+ ncplane_putstr_yx(np->n, 1, 2, "∨");
+ } else {
+ bool paused = entry->start == LIANA_TIMESTAMP_INVALID;
+ ncplane_putstr_yx(np->n, 1, 2, paused ? "^": ">");
+ }
+ }
+ ncplane_set_styles(np->n, NCSTYLE_NONE);
+
+ u32 width = ncplane_dim_x(np->n);
+ u32 height = ncplane_dim_y(np->n);
+
+ // ━ ╸╺ ╼ ╾ ╴╶
+ //ncplane_set_fg_palindex(np->n, 8);
+ f32 percent = 1.f;
+ if (duration > 0) percent = MIN(pos / (f32)duration, 1.f);
+ u32 bar_width = width - 1;
+ u32 segments = roundf((bar_width * 4) * percent);
+ u32 head = segments % 4;
+ segments /= 4;
+ for (u32 i = 0; i < segments; i++) {
+ ncplane_putstr_yx(np->n, 2, i + 1, "━");
+ }
+ char *head_segment;
+ switch (head) {
+ case 0:
+ head_segment = " ";
+ break;
+ case 1:
+ head_segment = "╴";
+ break;
+ case 2:
+ head_segment = "╸";
+ break;
+ case 3:
+ head_segment = "╾";
+ break;
+ }
+ ncplane_putstr_yx(np->n, 2, segments + 1, head_segment);
+ ncplane_set_fg_default(np->n);
+
+ char timestr[16] = { 0 }; // max = 829128280:01:49\0
+ s32 offset = camu_print_time(timestr, sizeof(timestr), pos, true, 4);
+ ncplane_putstr_yx(np->n, 1, 4, timestr);
+ ncplane_putstr_yx(np->n, 1, 4 + offset, "⎹");
+
+ if (entry) {
+ cmc_ui_putnwstr_yx(np->n, 1, 6 + offset, MIN(entry->name.length, (u32)60), &entry->name);
+ }
+
+ bool show_remaining = false;
+ if (show_remaining) {
+ offset = camu_print_time(timestr, sizeof(timestr), remaining, true, 4);
+ } else {
+ offset = camu_print_time(timestr, sizeof(timestr), duration, true, 4);
+ }
+ ncplane_putstr_yx(np->n, 1, width - 4 - offset, "⎸");
+ // "=" Duration, "+" Segment, "-" Remaining.
+ if (show_remaining) {
+ ncplane_putstr_yx(np->n, 1, width - 3 - offset, "-");
+ } else {
+ ncplane_putstr_yx(np->n, 1, width - 3 - offset, "=");
+ }
+ ncplane_putstr_yx(np->n, 1, width - 2 - offset, timestr);
+
+ if (entry) {
+ bool visual_cached = false;
+ struct cmc_now_playing_visual *visual;
+ al_array_foreach_ptr(np->visuals, i, visual) {
+ if (visual->entry == entry) {
+ visual_cached = true;
+ break;
+ }
+ }
+
+ if (!visual_cached) {
+ if (entry->raw.data != NULL) {
+ al_array_push(np->visuals, (struct cmc_now_playing_visual){});
+ visual = &al_array_last(np->visuals);
+ visual->entry = entry;
+ u32 *rgba;
+ u32 pixel_width, pixel_height;
+ ncplane_pixel_geom(np->wave, &pixel_height, &pixel_width, NULL, NULL, NULL, NULL);
+ cmc_draw_waveform(entry, pixel_width, pixel_height, false, entry->raw.drawing, &rgba);
+ visual->visual = ncvisual_from_rgba(rgba, pixel_height, pixel_width * 4, pixel_width);
+ } else {
+ visual = NULL;
+ }
+ }
+
+ if (visual && entry != np->blitted) {
+ ncplane_erase(np->wave);
+ struct ncvisual_options vopts = { 0 };
+ vopts.blitter = NCBLIT_PIXEL;
+ vopts.scaling = NCSCALE_STRETCH;
+ vopts.flags = NCVISUAL_OPTION_NOINTERPOLATE;
+ vopts.n = np->wave;
+ vopts.x = 0;
+ vopts.y = 0;
+ ncvisual_blit(ncplane_notcurses(np->wave), visual->visual, &vopts);
+ np->blitted = entry;
+ }
+ }
+
+ u64 c = 0;
+ ncchannels_set_fg_default(&c);
+ //ncchannels_set_fg_palindex(&c, 8);
+ ncchannels_set_bg_default(&c);
+ ncchannels_set_bg_alpha(&c, 0);
+ ncchannels_set_fg_alpha(&c, 255);
+ ncplane_cursor_move_yx(np->n, 0, 0);
+ //ncplane_double_box(np->n, NCSTYLE_NONE, c, height - 1, width - 1, 0);
+ ncplane_light_box(np->n, NCSTYLE_NONE, c, height - 1, width - 1, 0);
+ //ncplane_set_fg_palindex(np->n, 8);
+ if (segments > 0 || head > 0) {
+ if (segments == 0 && head == 1) {
+ ncplane_putstr_yx(np->n, 2, 0, "├");
+ } else {
+ ncplane_putstr_yx(np->n, 2, 0, "┝");
+ }
+ if (segments == bar_width || (segments == bar_width - 1 && head > 0)) {
+ if (head == 1) {
+ ncplane_putstr_yx(np->n, 2, width - 1, "┤");
+ } else {
+ ncplane_putstr_yx(np->n, 2, width - 1, "┥");
+ }
+ }
+ }
+ ncplane_set_fg_default(np->n);
+}
diff --git a/src/fruits/cmc/ui/widgets/now_playing.h b/src/fruits/cmc/ui/widgets/now_playing.h
new file mode 100644
index 0000000..5993c59
--- /dev/null
+++ b/src/fruits/cmc/ui/widgets/now_playing.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <al/array.h>
+#include <notcurses/notcurses.h>
+
+struct cmc_now_playing_visual {
+ struct ncvisual *visual;
+ struct cmc_list_entry *entry;
+};
+
+struct cmc_now_playing {
+ struct ncplane *n;
+ bool tick;
+ u64 last_pos;
+ u32 segment_count;
+ struct ncplane *wave;
+ array(struct cmc_now_playing_visual) visuals;
+ struct cmc_list_entry *blitted;
+};
+
+void cmc_now_playing_init(struct cmc_now_playing *np);
+bool cmc_now_playing_layout(struct cmc_now_playing *np, struct ncplane *parent, u32 y);
+bool cmc_now_playing_tick(struct cmc_now_playing *np, struct cmc_list_entry *entry);
+void cmc_now_playing_erase(struct cmc_now_playing *np);
+void cmc_now_playing_render(struct cmc_now_playing *np, struct cmc_list_entry *entry);
diff --git a/src/fruits/cmc/ui/tile.h b/src/fruits/cmc/ui/widgets/tile.h
index 6f15c7d..6f15c7d 100644
--- a/src/fruits/cmc/ui/tile.h
+++ b/src/fruits/cmc/ui/widgets/tile.h
diff --git a/src/liana/server.c b/src/liana/server.c
index 9d1f42a..3670607 100644
--- a/src/liana/server.c
+++ b/src/liana/server.c
@@ -468,7 +468,7 @@ static void duration_signal_callback(void *userdata)
void lia_node_get_duration(struct lia_node *node)
{
- // It is vital we don't block during handler->init().
+ // It is vital we don't block the loop during handler->init().
nn_signal_init(&node->signal, node->server->loop, duration_signal_callback, node);
nn_signal_start(&node->signal);
cch_entry_get_handle(node->entry, &node->handle);
diff --git a/src/libsink/sink.c b/src/libsink/sink.c
index 2b9d585..b9b3aa6 100644
--- a/src/libsink/sink.c
+++ b/src/libsink/sink.c
@@ -1278,9 +1278,7 @@ static struct camu_sink_entry *get_entry_from_id(struct camu_sink *sink, u32 id)
{
struct camu_sink_entry *entry;
al_array_foreach(sink->entries, i, entry) {
- if (entry->id == id) {
- return entry;
- }
+ if (entry->id == id) return entry;
}
return NULL;
}
@@ -1291,10 +1289,9 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
struct camu_sink *sink = (struct camu_sink *)userdata;
(void)rpacket;
- nn_mutex_lock(&sink->lock);
-
u8 op = nn_packet_read_u8(packet);
if (op == LIANA_SINK_UNSET) {
+ nn_mutex_lock(&sink->lock);
if (sink->current) {
queue_cmd(sink, (struct camu_sink_cmd){
.op = EJECT_ENTRY,
@@ -1333,6 +1330,10 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
lia_client_connect(&entry->client, sink->loop, sink->type, &addr, port, node_id, seek_pos);
}
+ // Don't lock before calling client_connect() or we could deadlock in
+ // CLIENT_CLOSED on a failed socket_connect().
+ nn_mutex_lock(&sink->lock);
+
if (op == LIANA_SINK_BUFFER) {
goto out;
} else if (op == LIANA_SINK_BUFFER_AND_QUEUE) {
diff --git a/src/mixer/mixer.c b/src/mixer/mixer.c
index fbe3105..7e62f8d 100644
--- a/src/mixer/mixer.c
+++ b/src/mixer/mixer.c
@@ -14,7 +14,7 @@ static s32 data_callback(void *userdata, u8 *data, s32 frame_count, bool *silenc
struct camu_mixer *mixer = (struct camu_mixer *)userdata;
ptrdiff_t req = (ptrdiff_t)camu_audio_format_samples_to_bytes(&mixer->fmt.req, (size_t)frame_count);
#ifdef CAMU_MIXER_THREADED_START_STOP
- if (UNLIKELY(al_atomic_load(bool)(&mixer->paused, AL_ATOMIC_SEQ_CST))) {
+ if (UNLIKELY(al_atomic_load(bool)(&mixer->paused, AL_ATOMIC_RELAXED))) {
al_memset(data, 0, req);
*silence = MIXER_WANT_INITIAL_SILENCE;
} else {
diff --git a/src/screen/screen.c b/src/screen/screen.c
index 0e44dcd..f749bcb 100644
--- a/src/screen/screen.c
+++ b/src/screen/screen.c
@@ -188,8 +188,8 @@ static bool key_callback(void *userdata, u8 state, u16 button)
break;
case STELA_KEY_RIGHT:
if (SCREEN_MOD1(scr)) {
- f64 plus_ten_seconds = 10.0;
- scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &plus_ten_seconds);
+ f64 plus_five_seconds = 5.0;
+ scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &plus_five_seconds);
break;
}
// fallthrough
@@ -205,8 +205,8 @@ static bool key_callback(void *userdata, u8 state, u16 button)
f64 zero = 0.0;
scr->callback(scr->userdata, CAMU_SCREEN_PERCENT_SEEK, &zero);
} else {
- f64 minus_ten_seconds = -10.0;
- scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &minus_ten_seconds);
+ f64 minus_five_seconds = -5.0;
+ scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &minus_five_seconds);
}
break;
}