From c66c7c64ebd16287b892f8a780cffcabafba3799 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Mon, 7 Sep 2026 15:06:14 -0400 Subject: Better OSD behavior, lots of build stuff Signed-off-by: Andrew Opalach --- src/fruits/cmc/ui/widgets/now_playing.c | 21 +++++---- src/fruits/cmsrv/cmsrv.c | 76 +++++++++++++++++++++++---------- src/fruits/cmv/cmv.c | 22 +++++++--- src/fruits/cmv/create_icon.sh | 4 ++ src/fruits/cmv/meson.build | 10 +++++ 5 files changed, 96 insertions(+), 37 deletions(-) create mode 100755 src/fruits/cmv/create_icon.sh (limited to 'src/fruits') diff --git a/src/fruits/cmc/ui/widgets/now_playing.c b/src/fruits/cmc/ui/widgets/now_playing.c index 56405b3..1a4303d 100644 --- a/src/fruits/cmc/ui/widgets/now_playing.c +++ b/src/fruits/cmc/ui/widgets/now_playing.c @@ -162,23 +162,28 @@ void cmc_now_playing_render(struct cmc_now_playing *np, struct cmc_list_entry *e ncplane_putstr_yx(np->n, 1, x1 + 2, timestr); if (entry) { - u32 rslash = al_str_rfind(&entry->brief, '/'); str part; + u32 rslash = al_str_rfind(&entry->brief, '/'); if (rslash != AL_STR_NO_POS) { part = al_str_substr(&entry->brief, rslash + 1, entry->brief.length); } else { part = entry->brief; } - char *c_str = al_str_to_c_str(&part); + // x1 = Position of right side "|". + // x2 = First character of entry brief. s32 x2 = 6 + width1; - // @TODO: Can mid be negative. s32 mid = x1 - x2; - ncplane_putnstr_yx(np->n, 1, x2, MIN(al_strlen(c_str), (size_t)mid), c_str); - if (part.length >= mid) { - ncplane_putnstr_yx(np->n, 1, x2 + mid - 1, 1, "."); - ncplane_putnstr_yx(np->n, 1, x2 + mid - 2, 1, "."); + if (mid >= 1) { + char *c_str = al_str_to_c_str(&part); + ncplane_putnstr_yx(np->n, 1, x2, MIN(al_strlen(c_str), (size_t)mid), c_str); + if (part.length >= (u32)mid) { + ncplane_putnstr_yx(np->n, 1, x2 + mid - 1, 1, "."); + if (mid >= 2) { + ncplane_putnstr_yx(np->n, 1, x2 + mid - 2, 1, "."); + } + } + al_free(c_str); } - al_free(c_str); bool visual_cached = false; struct cmc_now_playing_visual *visual; diff --git a/src/fruits/cmsrv/cmsrv.c b/src/fruits/cmsrv/cmsrv.c index b0793d1..452254b 100644 --- a/src/fruits/cmsrv/cmsrv.c +++ b/src/fruits/cmsrv/cmsrv.c @@ -30,6 +30,7 @@ struct cmsrv { struct nn_line_processor cli; } local; #endif + bool use_ui; #ifdef CMSRV_USE_UI struct cmsrv_ui ui; struct nn_poll input_poll; @@ -43,14 +44,27 @@ static void close_cmsrv(struct cmsrv *s) nn_line_processor_stop(&s->local.cli); #endif #ifdef CMSRV_USE_UI - nn_poll_stop(&s->input_poll); - nn_timer_stop(&s->render_timer); + if (s->use_ui) { + nn_poll_stop(&s->input_poll); + nn_timer_stop(&s->render_timer); + } #endif camu_server_close(&s->server); nn_signal_stop(&s->quit_signal); } #ifdef CMSRV_LOCAL_SOCKET +static inline void try_parse_arg_from_line(str *line, u32 offset, s32 *n) +{ + str arg = al_str_substr(line, offset, line->length); + bool error; + s64 i = al_str_to_long(&arg, 10, &error); + if (!error) { + if (*n < 0) i = -i; + *n = CLAMP(i, (s64)INT32_MIN, (s64)INT32_MAX); + } +} + static u8 server_line_callback(void *userdata, str *line) { struct cmsrv *s = (struct cmsrv *)userdata; @@ -60,19 +74,13 @@ static u8 server_line_callback(void *userdata, str *line) } else if (al_str_cmp(line, &al_str_c(";NEXT"), 0, 5) == 0) { s32 n = 1; if (line->length > 6) { - str arg = al_str_substr(line, 6, line->length); - bool error; - s64 i = al_str_to_long(&arg, 10, &error); - if (!error) n = CLAMP(i, (s64)INT32_MIN, (s64)INT32_MAX); + try_parse_arg_from_line(line, 6, &n); } lia_list_skip(list, LIANA_SEQUENCE_ANY, n); } else if (al_str_cmp(line, &al_str_c(";PREV"), 0, 5) == 0) { s32 n = -1; if (line->length > 6) { - str arg = al_str_substr(line, 6, line->length); - bool error; - s64 i = -al_str_to_long(&arg, 10, &error); - if (!error) n = CLAMP(i, (s64)INT32_MIN, (s64)INT32_MAX); + try_parse_arg_from_line(line, 6, &n); } lia_list_skip(list, LIANA_SEQUENCE_ANY, n); } else if (al_str_cmp(line, &al_str_c(";SEEK"), 0, 5) == 0) { @@ -93,8 +101,18 @@ static u8 server_line_callback(void *userdata, str *line) lia_list_reverse(list); } else if (al_str_eq(line, &al_str_c(";CLEAR"))) { lia_list_clear(list); + } else if (al_str_cmp(line, &al_str_c(";START_AT"), 0, 9) == 0) { + if (line->length > 6) { + s32 n = 0; + try_parse_arg_from_line(line, 10, &n); + if (n > 0) { + camu_server_local_start_at(&s->server, n); + } + } } else if (al_str_eq(line, &al_str_c(";DISCONNECT"))) { lia_server_force_disconnect_nodes(&s->server.data.server); + } else if (al_str_eq(line, &al_str_c(";QUIT"))) { + close_cmsrv(s); } else { struct nn_packet *packet = nn_packet_create(); nn_packet_write_str(packet, line); @@ -156,10 +174,17 @@ static void quit_signal_callback(void *userdata) static struct cmsrv s = { 0 }; +static bool sigint_force = false; static void sigint_handler(s32 signum) { (void)signum; - nn_signal_send(&s.quit_signal); + if (sigint_force) { + exit(128 + SIGINT); + } else { + nn_signal_send(&s.quit_signal); + log_warn("Attempting graceful exit. ^C again to force exit."); + sigint_force = true; + } } #ifdef NAUNET_ON_WINDOWS @@ -172,11 +197,16 @@ s32 main(s32 argc, char *argv[]) return EXIT_FAILURE; } + s.use_ui = false; #ifdef CMSRV_USE_UI - if (!cmsrv_ui_init(&s.ui, &s.server)) { - return EXIT_FAILURE; + char *CMSRV_UI = getenv("CMSRV_UI"); + if (CMSRV_UI) s.use_ui = al_strscmp(CMSRV_UI, "1") == 0; + if (s.use_ui) { + if (!cmsrv_ui_init(&s.ui, &s.server)) { + return EXIT_FAILURE; + } + al_set_print(log_callback, &s); } - al_set_print(log_callback, &s); #endif #ifdef CAMU_HAVE_FFMPEG @@ -194,7 +224,7 @@ s32 main(s32 argc, char *argv[]) if (CMSRV_IP) addr = al_str_cr(CMSRV_IP); log_info("Serving from %.*s.", al_str_x(&addr)); - camu_server_init(&s.server, &s.loop); + camu_server_init(&s.server, &s.loop, false); if (!camu_server_listen(&s.server, NNWT_SOCKET_TCP, &addr, CAMU_PORT)) { return EXIT_FAILURE; } @@ -212,12 +242,14 @@ s32 main(s32 argc, char *argv[]) #endif #ifdef CMSRV_USE_UI - nn_poll_init(&s.input_poll, input_poll_callback, &s); - nn_poll_set(&s.input_poll, cmsrv_ui_get_input_fd(&s.ui), NNWT_POLL_READ); - nn_poll_start(&s.input_poll, &s.loop); - nn_timer_init(&s.render_timer, &s.loop, render_timer_callback, &s); - nn_timer_set_repeat(&s.render_timer, NNWT_TS_FROM_USEC(100000)); - nn_timer_again(&s.render_timer); + if (s.use_ui) { + nn_poll_init(&s.input_poll, input_poll_callback, &s); + nn_poll_set(&s.input_poll, cmsrv_ui_get_input_fd(&s.ui), NNWT_POLL_READ); + nn_poll_start(&s.input_poll, &s.loop); + nn_timer_init(&s.render_timer, &s.loop, render_timer_callback, &s); + nn_timer_set_repeat(&s.render_timer, NNWT_TS_FROM_USEC(100000)); + nn_timer_again(&s.render_timer); + } #endif nn_event_loop_run(&s.loop); @@ -226,7 +258,7 @@ s32 main(s32 argc, char *argv[]) nn_socket_close(&s.local.sock); #endif #ifdef CMSRV_USE_UI - cmsrv_ui_close(&s.ui); + if (s.use_ui) cmsrv_ui_close(&s.ui); #endif camu_server_free(&s.server); nn_event_loop_destroy(&s.loop); diff --git a/src/fruits/cmv/cmv.c b/src/fruits/cmv/cmv.c index 74e3555..333588e 100644 --- a/src/fruits/cmv/cmv.c +++ b/src/fruits/cmv/cmv.c @@ -18,7 +18,7 @@ #include "icon.h" #endif -#ifndef NAUNET_ON_WINDOWS +#if !defined NAUNET_ON_WINDOWS && !defined NAUNET_ON_ANDROID // https://unix.stackexchange.com/a/16884 #define PID_MAX_STR 7 // 0x400000 (2^22). static str CMV_UNIX_PATH = al_str_c("/tmp/cmv_sock_"); @@ -45,6 +45,7 @@ static void exit_callback(void *userdata, struct camu_desktop *desktop) static nn_thread_result NNWT_THREADCALL event_loop_thread(void *userdata) { + nn_thread_set_priority(NNWT_THREAD_SCHED_FIFO, 40); struct cmv *c = (struct cmv *)userdata; nn_thread_set_name("event_loop"); nn_event_loop_run(&c->loop); @@ -174,9 +175,16 @@ s32 window_system_main(u32 argc, str *argv, void *extra) prefs->language.audio = CAMU_LANG_JAPANESE; prefs->language.subtitles = CAMU_LANG_ENGLISH; +#ifdef CAMU_DIRECT_MODE +#ifdef CAMU_SINK_ONLY +#error "Sink-only and DIRECT_MODE are incompatible" +#endif + if (!local) al_assert("Cannot connect to a server in DIRECT_MODE" && false); +#endif + #ifndef CAMU_SINK_ONLY if (local) { - camu_server_init(&c.server, &c.loop); + camu_server_init(&c.server, &c.loop, local); #ifdef CAMU_DIRECT_MODE camu_server_bind_direct(&c.server); #else @@ -212,8 +220,8 @@ s32 window_system_main(u32 argc, str *argv, void *extra) c.desktop.userdata = &c; } char *SINK_FORCE_LOCAL = getenv("SINK_FORCE_LOCAL"); - if (SINK_FORCE_LOCAL && al_strscmp(SINK_FORCE_LOCAL, "1") == 0) { - c.desktop.sink.local = true; + if (SINK_FORCE_LOCAL) { + c.desktop.sink.local = al_strscmp(SINK_FORCE_LOCAL, "1") == 0; } else { c.desktop.sink.local = local; } @@ -232,9 +240,9 @@ s32 window_system_main(u32 argc, str *argv, void *extra) nn_packet_free(packet); } if (extra) { - u32 start_index = *(u32 *)extra; + s32 start_index = *(s32 *)extra; if (start_index != 0) { - camu_server_local_set_index(&c.server, start_index); + camu_server_local_start_at(&c.server, start_index); } } } @@ -306,7 +314,7 @@ s32 main(s32 argc, char *argv[]) for (s32 i = 0; i < argc; i++) { str arg; #ifdef NAUNET_ON_WINDOWS - if (!al_str_from_wstr(&arg, &al_wstr_cr(argv[i]))) { // This is so broken. + if (!al_str_from_wstr(&arg, &al_wstr_cr(argv[i]))) { log_error("Failed to convert argument #%i from a wide string.", i); continue; } diff --git a/src/fruits/cmv/create_icon.sh b/src/fruits/cmv/create_icon.sh new file mode 100755 index 0000000..08aa8eb --- /dev/null +++ b/src/fruits/cmv/create_icon.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env sh +for size in {16,32,64,128}; do + magick sink.png -resize $sizex$size -set filename:f "%[t]-%[w]x%[h]" "%[filename:f].png" +done diff --git a/src/fruits/cmv/meson.build b/src/fruits/cmv/meson.build index 301fa58..a9eda61 100644 --- a/src/fruits/cmv/meson.build +++ b/src/fruits/cmv/meson.build @@ -21,10 +21,20 @@ elif is_windows windows = import('windows') cmv_src += [windows.compile_resources('icon.rc')] if not is_msvc + cmv_args += ['-mwindows', '-municode'] cmv_link_args += ['-mwindows', '-municode', '-static', '-static-libgcc', '-static-libstdc++'] endif endif +install_data(join_paths(meson.source_root(), 'package/sink.desktop'), install_tag: 'xdg', + install_dir: join_paths(get_option('datadir'), 'applications')) + +foreach size: ['16x16', '32x32', '64x64', '128x128', '256x256'] + icon_dir = join_paths(get_option('datadir'), 'icons', 'hicolor', size, 'apps') + install_data('sink-' + size + '.png', install_tag: 'xdg', + install_dir: icon_dir, rename: 'sink.png') +endforeach + if is_android cmv_args += ['-DAPPLICATION_NAME="CTV"'] shared_library('ctv', cmv_src, dependencies: cmv_deps, c_args: cmv_args, link_args: cmv_link_args) -- cgit v1.2.3-101-g0448