diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index d87b7d7c..32b02250 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -968,7 +968,7 @@ namespace nvhttp { current_appid = 0; } tree.put("root.currentgame", current_appid); - tree.put("root.state", proc::proc.running() > 0 ? "SUNSHINE_SERVER_BUSY" : "SUNSHINE_SERVER_FREE"); + tree.put("root.state", current_appid > 0 ? "SUNSHINE_SERVER_BUSY" : "SUNSHINE_SERVER_FREE"); } else { tree.put("root.currentgame", 0); tree.put("root.state", "SUNSHINE_SERVER_FREE"); @@ -1053,17 +1053,27 @@ namespace nvhttp { auto named_cert_p = get_verified_cert(request); if (!!(named_cert_p->perm & PERM::_all_actions)) { auto current_appid = proc::proc.running(); - auto input_only_id_int = proc::input_only_app_id; - auto should_hide_inactive_apps = config::input.enable_input_only_mode && current_appid > 0 && current_appid != input_only_id_int; + auto should_hide_inactive_apps = config::input.enable_input_only_mode && current_appid > 0 && current_appid != proc::input_only_app_id; for (auto &app : proc::proc.get_apps()) { auto appid = util::from_view(app.id); - if (should_hide_inactive_apps && appid != current_appid && appid != input_only_id_int) { - continue; + if (should_hide_inactive_apps) { + if ( + appid != current_appid + && appid != proc::input_only_app_id + && appid != proc::terminate_app_id + ) { + continue; + } + } else { + if (appid == proc::terminate_app_id) { + continue; + } } + pt::ptree app_node; app_node.put("IsHdrSupported"s, video::active_hevc_mode == 3 ? 1 : 0); - if (should_hide_inactive_apps && appid != input_only_id_int) { + if (should_hide_inactive_apps && appid != proc::input_only_app_id && appid != proc::terminate_app_id) { app_node.put("AppTitle"s, "Resume: "s + app.name); } else { app_node.put("AppTitle"s, app.name); @@ -1114,7 +1124,7 @@ namespace nvhttp { auto perm = PERM::launch; // If we have already launched an app, we should allow clients with view permission to join the input only or current app's session. - if (current_appid > 0 && (is_input_only || appid == current_appid)) { + if (current_appid > 0 && appid != proc::terminate_app_id && (is_input_only || appid == current_appid)) { perm = PERM::_allow_view; } @@ -1140,6 +1150,17 @@ namespace nvhttp { } if (!is_input_only) { + // Special handling for the "terminate" app + if (config::input.enable_input_only_mode && appid == proc::terminate_app_id) { + proc::proc.terminate(); + + tree.put("root.resume", 0); + tree.put("root..status_code", 410); + tree.put("root..status_message", "App terminated."); + + return; + } + if (current_appid > 0 && current_appid != proc::input_only_app_id && appid != current_appid) { tree.put("root.resume", 0); tree.put("root..status_code", 400); diff --git a/src/process.cpp b/src/process.cpp index 1a6cf2d8..7e7a83b7 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -51,6 +51,8 @@ namespace proc { int input_only_app_id = -1; std::string input_only_app_id_str; + int terminate_app_id = -1; + std::string terminate_app_id_str; #ifdef _WIN32 VDISPLAY::DRIVER_STATUS vDisplayDriverStatus = VDISPLAY::DRIVER_STATUS::UNKNOWN; @@ -930,38 +932,74 @@ namespace proc { std::vector apps; int i = 0; - // Input Only entry if (config::input.enable_input_only_mode) { - proc::ctx_t ctx; - // ctx.uuid = ""; // We're not using uuid for this special entry - ctx.name = "Remote Input"; - ctx.image_path = parse_env_val(this_env, "input_only.png"); - ctx.virtual_display = false; - ctx.scale_factor = 100; - ctx.use_app_identity = false; - ctx.per_client_app_identity = false; - ctx.allow_client_commands = false; + // Input Only entry + { + proc::ctx_t ctx; + // ctx.uuid = ""; // We're not using uuid for this special entry + ctx.name = "Remote Input"; + ctx.image_path = parse_env_val(this_env, "input_only.png"); + ctx.virtual_display = false; + ctx.scale_factor = 100; + ctx.use_app_identity = false; + ctx.per_client_app_identity = false; + ctx.allow_client_commands = false; - ctx.elevated = false; - ctx.auto_detach = true; - ctx.wait_all = true; - ctx.exit_timeout = 5s; + ctx.elevated = false; + ctx.auto_detach = true; + ctx.wait_all = true; + ctx.exit_timeout = 5s; - auto possible_ids = calculate_app_id(ctx.name, ctx.image_path, i++); - if (ids.count(std::get<0>(possible_ids)) == 0) { - // Avoid using index to generate id if possible - ctx.id = std::get<0>(possible_ids); + auto possible_ids = calculate_app_id(ctx.name, ctx.image_path, i++); + if (ids.count(std::get<0>(possible_ids)) == 0) { + // Avoid using index to generate id if possible + ctx.id = std::get<0>(possible_ids); + } + else { + // Fallback to include index on collision + ctx.id = std::get<1>(possible_ids); + } + ids.insert(ctx.id); + + input_only_app_id_str = ctx.id; + input_only_app_id = util::from_view(ctx.id); + + apps.emplace_back(std::move(ctx)); } - else { - // Fallback to include index on collision - ctx.id = std::get<1>(possible_ids); + + // Terminate entry + { + proc::ctx_t ctx; + // ctx.uuid = ""; // We're not using uuid for this special entry + ctx.name = "Terminate"; + ctx.image_path = parse_env_val(this_env, "terminate.png"); + ctx.virtual_display = false; + ctx.scale_factor = 100; + ctx.use_app_identity = false; + ctx.per_client_app_identity = false; + ctx.allow_client_commands = false; + + ctx.elevated = false; + ctx.auto_detach = true; + ctx.wait_all = true; + ctx.exit_timeout = 5s; + + auto possible_ids = calculate_app_id(ctx.name, ctx.image_path, i++); + if (ids.count(std::get<0>(possible_ids)) == 0) { + // Avoid using index to generate id if possible + ctx.id = std::get<0>(possible_ids); + } + else { + // Fallback to include index on collision + ctx.id = std::get<1>(possible_ids); + } + ids.insert(ctx.id); + + terminate_app_id_str = ctx.id; + terminate_app_id = util::from_view(ctx.id); + + apps.emplace_back(std::move(ctx)); } - ids.insert(ctx.id); - - input_only_app_id_str = ctx.id; - input_only_app_id = util::from_view(ctx.id); - - apps.emplace_back(std::move(ctx)); } // Virtual Display entry diff --git a/src/process.h b/src/process.h index a3cae6e7..6e438877 100644 --- a/src/process.h +++ b/src/process.h @@ -182,6 +182,9 @@ namespace proc { terminate_process_group(boost::process::v1::child &proc, boost::process::v1::group &group, std::chrono::seconds exit_timeout); extern proc_t proc; + extern int input_only_app_id; extern std::string input_only_app_id_str; + extern int terminate_app_id; + extern std::string terminate_app_id_str; } // namespace proc diff --git a/src_assets/common/assets/terminate.png b/src_assets/common/assets/terminate.png new file mode 100644 index 00000000..961ac963 Binary files /dev/null and b/src_assets/common/assets/terminate.png differ