diff --git a/cmake/prep/init.cmake b/cmake/prep/init.cmake index d3e9fdcad11..ef1248209ac 100644 --- a/cmake/prep/init.cmake +++ b/cmake/prep/init.cmake @@ -12,6 +12,14 @@ elseif (UNIX) set(SUNSHINE_EXECUTABLE_PATH "sunshine") endif() + # Auto-detect init system for desktop file + find_package(Systemd QUIET) + if(SYSTEMD_FOUND) + set(SUNSHINE_DESKTOP_EXEC "/usr/bin/env systemctl start --u app-${PROJECT_FQDN}") + else() + set(SUNSHINE_DESKTOP_EXEC "${CMAKE_INSTALL_FULL_BINDIR}/${SUNSHINE_EXECUTABLE_PATH}") + endif() + if(SUNSHINE_BUILD_FLATPAK) set(SUNSHINE_SERVICE_START_COMMAND "ExecStart=flatpak run --command=sunshine ${PROJECT_FQDN}") set(SUNSHINE_SERVICE_STOP_COMMAND "ExecStop=flatpak kill ${PROJECT_FQDN}") diff --git a/packaging/linux/dev.lizardbyte.app.Sunshine.desktop b/packaging/linux/dev.lizardbyte.app.Sunshine.desktop index 591f60acb87..32b5039942c 100644 --- a/packaging/linux/dev.lizardbyte.app.Sunshine.desktop +++ b/packaging/linux/dev.lizardbyte.app.Sunshine.desktop @@ -2,7 +2,7 @@ Actions=RunInTerminal; Categories=RemoteAccess;Network; Comment=@PROJECT_DESCRIPTION@ -Exec=/usr/bin/env systemctl start --u app-@PROJECT_FQDN@ +Exec=@SUNSHINE_DESKTOP_EXEC@ Icon=@SUNSHINE_DESKTOP_ICON@ StartupWMClass=@PROJECT_FQDN@ Keywords=gamestream;stream;moonlight;remote play; diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index 0ef9df41862..033679664da 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -177,6 +177,10 @@ namespace nvhttp { // Set by TLS verify callback, read by launch/resume handler (single-threaded HTTPS server) std::string last_verified_client_cert; ///< Last client certificate accepted by the TLS verify callback. // NOSONAR(cpp:S5421) - intentionally mutable global + // Saved originals for per-app config overrides + std::string saved_output_name; ///< Original config::video.output_name before per-app override. // NOSONAR(cpp:S5421) - intentionally mutable global + bool saved_stream_audio {true}; ///< Original config::audio.stream before per-app override. // NOSONAR(cpp:S5421) - intentionally mutable global + /** * @brief Case-insensitive map used for HTTP headers and query parameters. */ @@ -1037,6 +1041,22 @@ namespace nvhttp { host_audio = util::from_view(get_arg(args, "localAudioPlayMode")); auto launch_session = make_launch_session(host_audio, args); + // Save originals before per-app config overrides + saved_output_name = config::video.output_name; + saved_stream_audio = config::audio.stream; + + for (auto &app : proc::proc.get_apps()) { + if (app.id == std::to_string(appid)) { + if (!app.output_name.empty()) { + config::video.output_name = app.output_name; + } + if (app.stream_audio.has_value()) { + config::audio.stream = app.stream_audio.value(); + } + break; + } + } + if (rtsp_stream::session_count() == 0) { // The display should be restored in case something fails as there are no other sessions. revert_display_configuration = true; @@ -1226,6 +1246,10 @@ namespace nvhttp { // The config needs to be reverted regardless of whether "proc::proc.terminate()" was called or not. display_device::revert_configuration(); + + // Restore per-app config overrides to global defaults + config::video.output_name = saved_output_name; + config::audio.stream = saved_stream_audio; } /** diff --git a/src/process.cpp b/src/process.cpp index 3a2c76d682c..b47a5c72d8b 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -690,11 +690,13 @@ namespace proc { auto name = parse_env_val(this_env, app_node.get("name"s)); auto cmd = app_node.get_optional("cmd"s); auto image_path = app_node.get_optional("image-path"s); + auto output_name = app_node.get_optional("output-name"s); auto working_dir = app_node.get_optional("working-dir"s); auto elevated = app_node.get_optional("elevated"s); auto auto_detach = app_node.get_optional("auto-detach"s); auto wait_all = app_node.get_optional("wait-all"s); auto exit_timeout = app_node.get_optional("exit-timeout"s); + auto stream_audio = app_node.get_optional("stream-audio"s); std::vector prep_cmds; if (!exclude_global_prep.value_or(false)) { @@ -760,6 +762,14 @@ namespace proc { ctx.image_path = parse_env_val(this_env, *image_path); } + if (output_name) { + ctx.output_name = parse_env_val(this_env, *output_name); + } + + if (stream_audio) { + ctx.stream_audio = *stream_audio; + } + ctx.elevated = elevated.value_or(false); ctx.auto_detach = auto_detach.value_or(true); ctx.wait_all = wait_all.value_or(true); diff --git a/src/process.h b/src/process.h index 75a9de09ef8..0b7baadd794 100644 --- a/src/process.h +++ b/src/process.h @@ -75,6 +75,8 @@ namespace proc { std::string cmd; ///< Command line used to launch the application. std::string working_dir; ///< Working dir. std::string output; ///< Captured output from the launched process. + std::string output_name; ///< Display output name override for this app. + std::optional stream_audio; ///< Audio streaming override for this app (std::nullopt = use global config). std::string image_path; ///< Image path. std::string id; ///< Stable identifier for the configured application. bool elevated; ///< Whether the process should be launched elevated. diff --git a/src_assets/common/assets/web/apps.html b/src_assets/common/assets/web/apps.html index 32b3e0af9ed..2c90ab823d8 100644 --- a/src_assets/common/assets/web/apps.html +++ b/src_assets/common/assets/web/apps.html @@ -319,6 +319,22 @@