#define AL_LOG_SECTION "cmv" #include #include #include #include #include "../../libsink/desktop.h" #include "../../server/common.h" #ifndef CAMU_SINK_ONLY #include "../../server/server.h" #endif #include "../../util/color_palette.h" #ifdef CAMU_HAVE_FFMPEG #include "../../codec/ffmpeg/common.h" #endif #ifdef STELA_ICON_AT_RUNTIME #include "icon.h" #endif #ifndef NAUNET_ON_WINDOWS // 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_"); #endif struct cmv { struct nn_event_loop loop; struct camu_desktop desktop; #ifndef CAMU_SINK_ONLY struct camu_server server; #endif }; static void exit_callback(void *userdata, struct camu_desktop *desktop) { struct cmv *c = (struct cmv *)userdata; (void)desktop; #ifndef CAMU_SINK_ONLY camu_server_close(&c->server); #else (void)c; #endif } static nn_thread_result NNWT_THREADCALL event_loop_thread(void *userdata) { struct cmv *c = (struct cmv *)userdata; nn_thread_set_name("event_loop"); nn_event_loop_run(&c->loop); return 0; } static bool decode_addr(str *addr, str *enc) { char value[4]; // max = 255. for (u32 i = 0; i < 8; i += 2) { str sub = al_str_substr(enc, i, i + 2); bool error; s64 l = al_str_to_long(&sub, 16, &error); al_assert(l >= 0 && l <= 255); if (error) return false; al_snprintf(value, sizeof(value), "%hhu", (u8)l); al_str_cat(addr, &al_str_cs(value)); if (i != 6) { al_str_cat(addr, &al_str_c(".")); } } return true; } // sink-p12_NOTE-976502a7.exe static bool parse_exe_name(str *exe_name, str *addr) { u32 index = al_str_rfind(exe_name, '/'); if (index == AL_STR_NO_POS) { if ((index = al_str_rfind(exe_name, '\\')) == AL_STR_NO_POS) { goto def; } } str sub = al_str_substr(exe_name, index + 1, exe_name->length); if (al_str_cmp(&sub, &al_str_c("sink-"), 0, 5) != 0) { goto def; } sub = al_str_substr(&sub, 5, sub.length); // Skip version. if ((index = al_str_find(&sub, '-')) == AL_STR_NO_POS) { goto def; } sub = al_str_substr(&sub, index + 1, sub.length); if ((index = al_str_find(&sub, '.')) != AL_STR_NO_POS) { sub = al_str_substr(&sub, 0, index); } al_str_from(addr, ""); decode_addr(addr, &sub); log_debug("Parsed address %.*s from exe filename.", al_str_x(addr)); return true; def: return false; } static struct cmv c = { 0 }; s32 window_system_main(u32 argc, str *argv, void *extra) { (void)extra; bool local = argc > 1; #ifdef CAMU_SINK_ONLY if (local) { log_warn("Got arguments in a sink-only configuration."); return EXIT_FAILURE; } #endif #ifdef CAMU_HAVE_FFMPEG camu_ff_common_init(); #endif s32 ret = EXIT_SUCCESS; nn_event_loop_init(&c.loop); u8 type; str addr = al_str_null(); if (local) { #ifdef NAUNET_ON_WINDOWS type = NNWT_SOCKET_TCP; al_str_clone(&addr, &al_str_c("127.0.0.1")); #else type = NNWT_SOCKET_UNIX; al_str_clone(&addr, &CMV_UNIX_PATH); char pid_str[PID_MAX_STR]; al_snprintf(pid_str, PID_MAX_STR, "%x", getpid()); al_str_cat(&addr, &al_str_cs(pid_str)); #endif } else if (parse_exe_name(&argv[0], &addr)) { type = NNWT_SOCKET_TCP; } else { type = NNWT_SOCKET_TCP; char *SINK_IP = getenv("SINK_IP"); al_str_from(&addr, ""); if (!SINK_IP || !decode_addr(&addr, &al_str_cr(SINK_IP))) { al_str_cat(&addr, &al_str_c("127.0.0.1")); } } struct lia_prefs *prefs = &c.desktop.sink.prefs; #ifdef CAMU_HAVE_SUBTITLES prefs->enabled = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_SUBTITLE); #else prefs->enabled = (1 << CAMU_STREAM_AUDIO); #endif char *SINK_NO_VIDEO = getenv("SINK_NO_VIDEO"); if (SINK_NO_VIDEO && al_strscmp(SINK_NO_VIDEO, "1") == 0) { } else { prefs->enabled |= (1 << CAMU_STREAM_VIDEO); } prefs->language.audio = CAMU_LANG_JAPANESE; prefs->language.subtitles = CAMU_LANG_ENGLISH; #ifndef CAMU_SINK_ONLY if (local) { camu_server_init(&c.server, &c.loop); #ifdef CAMU_DIRECT_MODE camu_server_bind_direct(&c.server); #else if (!camu_server_listen(&c.server, type, &addr, CAMU_PORT)) { ret = EXIT_FAILURE; goto out; } #endif } #endif char *home_env = getenv("HOME"); if (home_env) { str color_palette; al_str_from(&color_palette, home_env); al_str_cat(&color_palette, &al_str_c("/.config/colors.json")); camu_colors_maybe_init(&color_palette); al_str_free(&color_palette); } str window_name; al_str_from(&window_name, "Sink ("); if (local) { al_str_cat(&window_name, &al_str_c("cmv)")); } else { al_str_cat(&window_name, &addr); al_str_cat(&window_name, &al_str_c(")")); } char *window_name_c = al_str_to_c_str(&window_name); bool desktop_opened = camu_desktop_open(&c.desktop, window_name_c); al_free(window_name_c); al_str_free(&window_name); if (!desktop_opened) { ret = EXIT_FAILURE; goto out; } str *name = local ? &al_str_c("local") : &al_str_c("desktop"); if (local) { c.desktop.exit_callback = exit_callback; 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; } else { c.desktop.sink.local = local; } if (!camu_desktop_connect(&c.desktop, name, &c.loop, type, &addr, CAMU_PORT)) { ret = EXIT_FAILURE; goto out; } if (local) { for (u32 i = 1; i < argc; i++) { struct nn_packet *packet = nn_packet_create(); nn_packet_write_str(packet, &argv[i]); #ifndef CAMU_SINK_ONLY camu_server_local_add(&c.server, packet); #endif nn_packet_free(packet); } } struct nn_thread thread; nn_thread_create(&thread, event_loop_thread, &c); while (camu_desktop_tick(&c.desktop)) {} camu_desktop_stop(&c.desktop); nn_thread_join(&thread); out: al_str_free(&addr); camu_desktop_free(&c.desktop); #ifndef CAMU_SINK_ONLY camu_server_free(&c.server); #endif nn_event_loop_destroy(&c.loop); return ret; } static bool sigint_force = false; static void sigint_handler(s32 signum) { (void)signum; if (sigint_force) { exit(128 + SIGINT); } else { c.desktop.should_quit = 1; log_warn("Attempting graceful exit. ^C again to force exit."); sigint_force = true; } } #ifdef NAUNET_ON_WINDOWS s32 wmain(s32 argc, wchar_t **argv) #else s32 main(s32 argc, char *argv[]) #endif { if (!nn_common_init("cmv_main")) { return EXIT_FAILURE; } if (!stl_global_init(false)) { nn_common_close(); return EXIT_FAILURE; } #ifdef STELA_ICON_AT_RUNTIME stl_set_icon_data(icon_256, icon_256_len); #endif signal(SIGINT, sigint_handler); array(str) args; al_array_init(args); al_array_reserve(args, argc); 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. log_error("Failed to convert argument #%i from a wide string.", i); continue; } #else al_str_from(&arg, argv[i]); #endif al_array_push(args, arg); } s32 ret = stl_swallow_main(args.count, args.data, window_system_main, NULL); stl_global_close(); str *arg; al_array_foreach_ptr(args, i, arg) { al_str_free(arg); } al_array_free(args); nn_common_close(); return ret; }