From 5d3ef208c018d3cff9601ac006fbe28a82a5d2d4 Mon Sep 17 00:00:00 2001 From: ShadowLemoon <119576779+ShadowLemoon@users.noreply.github.com> Date: Wed, 6 May 2026 23:07:33 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix(nvhttp):=20=E4=BF=AE=E5=A4=8D=E6=81=A2?= =?UTF-8?q?=E5=A4=8D=E6=97=B6app=5Fid=E4=B8=8D=E5=90=8C=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E5=BA=94=E7=94=A8=E6=84=8F=E5=A4=96=E9=80=80?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/nvhttp.cpp | 130 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 91 insertions(+), 39 deletions(-) diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index e0442157390..706f86083ef 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -6,6 +6,7 @@ #define BOOST_BIND_GLOBAL_PLACEHOLDERS // standard includes +#include #include #include #include @@ -1777,12 +1778,42 @@ namespace nvhttp { } } - void - launch(bool &host_audio, resp_https_t response, req_https_t request) { - print_req(request); + static bool + has_required_launch_params(const args_t &args, bool require_appid) { + return args.find("rikey"s) != std::end(args) + && args.find("rikeyid"s) != std::end(args) + && args.find("localAudioPlayMode"s) != std::end(args) + && (!require_appid || args.find("appid"s) != std::end(args)); + } - print_request_ip(request, "Launch request"); + static bool + has_required_resume_params(const args_t &args) { + return args.find("rikey"s) != std::end(args) + && args.find("rikeyid"s) != std::end(args); + } + + static int + find_desktop_app_id() { + const auto &apps = proc::proc.get_apps(); + auto desktop = std::find_if(std::begin(apps), std::end(apps), [](const auto &app) { + return app.image_path == "desktop" || app.name == "Desktop"; + }); + if (desktop == std::end(apps)) { + desktop = std::find_if(std::begin(apps), std::end(apps), [](const auto &app) { + return app.cmd.empty(); + }); + } + + if (desktop == std::end(apps)) { + return 0; + } + + return util::from_view(desktop->id); + } + + void + launch_app(bool &host_audio, resp_https_t response, req_https_t request, const args_t &args, int appid) { pt::ptree tree; bool need_to_restore_display_state { false }; auto g = util::fail_guard([&]() { @@ -1801,21 +1832,6 @@ namespace nvhttp { } }); - auto args = request->parse_query_string(); - if ( - args.find("rikey"s) == std::end(args) || - args.find("rikeyid"s) == std::end(args) || - args.find("localAudioPlayMode"s) == std::end(args) || - args.find("appid"s) == std::end(args)) { - tree.put("root.resume", 0); - tree.put("root..status_code", 400); - tree.put("root..status_message", "Missing a required launch parameter"); - - return; - } - - auto appid = util::from_view(get_arg(args, "appid")); - auto current_appid = proc::proc.running(); if (current_appid > 0) { tree.put("root.resume", 0); @@ -1835,8 +1851,11 @@ namespace nvhttp { return; } - host_audio = util::from_view(get_arg(args, "localAudioPlayMode")); + if (args.find("localAudioPlayMode"s) != std::end(args)) { + host_audio = util::from_view(get_arg(args, "localAudioPlayMode")); + } const auto launch_session = make_launch_session(host_audio, args); + launch_session->appid = appid; // 获取客户端证书UUID(稳定的客户端标识符) std::string client_cert_uuid = get_client_cert_uuid_from_request(request); @@ -1877,15 +1896,13 @@ namespace nvhttp { return; } - if (appid > 0) { - auto err = proc::proc.execute(appid, launch_session); - if (err) { - tree.put("root..status_code", err); - tree.put("root..status_message", "Failed to start the specified application"); - tree.put("root.gamesession", 0); + auto err = proc::proc.execute(appid, launch_session); + if (err) { + tree.put("root..status_code", err); + tree.put("root..status_message", "Failed to start the specified application"); + tree.put("root.gamesession", 0); - return; - } + return; } tree.put("root..status_code", 200); @@ -1916,6 +1933,35 @@ namespace nvhttp { need_to_restore_display_state = false; } + void + launch(bool &host_audio, resp_https_t response, req_https_t request) { + print_req(request); + + print_request_ip(request, "Launch request"); + + auto args = request->parse_query_string(); + if (!has_required_launch_params(args, true)) { + pt::ptree tree; + auto g = util::fail_guard([&]() { + std::ostringstream data; + + pt::write_xml(data, tree); + response->write(data.str()); + response->close_connection_after_response = true; + }); + + tree.put("root.resume", 0); + tree.put("root..status_code", 400); + tree.put("root..status_message", "Missing a required launch parameter"); + + return; + } + + auto appid = util::from_view(get_arg(args, "appid")); + + launch_app(host_audio, response, request, args, appid); + } + void resume(bool &host_audio, resp_https_t response, req_https_t request) { print_req(request); @@ -1941,23 +1987,28 @@ namespace nvhttp { response->close_connection_after_response = true; }); - auto current_appid = proc::proc.running(); - if (current_appid == 0) { + auto args = request->parse_query_string(); + if (!has_required_resume_params(args)) { tree.put("root.resume", 0); - tree.put("root..status_code", 503); - tree.put("root..status_message", "No running app to resume"); + tree.put("root..status_code", 400); + tree.put("root..status_message", "Missing a required resume parameter"); return; } - auto args = request->parse_query_string(); - if ( - args.find("rikey"s) == std::end(args) || - args.find("rikeyid"s) == std::end(args)) { - tree.put("root.resume", 0); - tree.put("root..status_code", 400); - tree.put("root..status_message", "Missing a required resume parameter"); + auto current_appid = proc::proc.running(); + if (current_appid == 0) { + auto desktop_appid = find_desktop_app_id(); + if (desktop_appid <= 0) { + tree.put("root.resume", 0); + tree.put("root..status_code", 404); + tree.put("root..status_message", "No running app to resume and Desktop app not found"); + + return; + } + g.disable(); + launch_app(host_audio, response, request, args, desktop_appid); return; } @@ -1969,6 +2020,7 @@ namespace nvhttp { host_audio = util::from_view(get_arg(args, "localAudioPlayMode")); } const auto launch_session = make_launch_session(host_audio, args); + launch_session->appid = current_appid; // Get client certificate UUID (stable client identifier) and store it in env std::string client_cert_uuid = get_client_cert_uuid_from_request(request); From 34d79e89b31a9863db645254f713518ed8412511 Mon Sep 17 00:00:00 2001 From: ShadowLemoon <119576779+ShadowLemoon@users.noreply.github.com> Date: Wed, 6 May 2026 23:34:06 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dxml=E5=BD=A2=E7=8A=B6?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/nvhttp.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index 706f86083ef..7365c54f59c 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -1813,8 +1813,9 @@ namespace nvhttp { } void - launch_app(bool &host_audio, resp_https_t response, req_https_t request, const args_t &args, int appid) { + launch_app(bool &host_audio, resp_https_t response, req_https_t request, const args_t &args, int appid, const char *result_node_name) { pt::ptree tree; + const auto result_node = "root."s + result_node_name; bool need_to_restore_display_state { false }; auto g = util::fail_guard([&]() { std::ostringstream data; @@ -1834,7 +1835,7 @@ namespace nvhttp { auto current_appid = proc::proc.running(); if (current_appid > 0) { - tree.put("root.resume", 0); + tree.put(result_node, 0); tree.put("root..status_code", 400); tree.put("root..status_message", "An app is already running on this host"); @@ -1844,7 +1845,7 @@ namespace nvhttp { // Early validation of AppID to prevent starting VDD or other expensive operations // if the requested app does not exist. if (proc::proc.get_app_name(appid).empty()) { - tree.put("root.resume", 0); + tree.put(result_node, 0); tree.put("root..status_code", 404); tree.put("root..status_message", "App not found"); BOOST_LOG(error) << "Launch couldn't find app with ID ["sv << appid << ']'; @@ -1879,7 +1880,7 @@ namespace nvhttp { if (video::probe_encoders()) { tree.put("root..status_code", 503); tree.put("root..status_message", "Failed to initialize video capture/encoding. Is a display connected and turned on?"); - tree.put("root.gamesession", 0); + tree.put(result_node, 0); return; } @@ -1891,7 +1892,7 @@ namespace nvhttp { tree.put("root..status_code", 403); tree.put("root..status_message", "Encryption is mandatory for this host but unsupported by the client"); - tree.put("root.gamesession", 0); + tree.put(result_node, 0); return; } @@ -1900,7 +1901,7 @@ namespace nvhttp { if (err) { tree.put("root..status_code", err); tree.put("root..status_message", "Failed to start the specified application"); - tree.put("root.gamesession", 0); + tree.put(result_node, 0); return; } @@ -1909,7 +1910,7 @@ namespace nvhttp { tree.put("root.sessionUrl0", launch_session->rtsp_url_scheme + net::addr_to_url_escaped_string(request->local_endpoint().address()) + ':' + std::to_string(net::map_port(rtsp_stream::RTSP_SETUP_PORT))); - tree.put("root.gamesession", 1); + tree.put(result_node, 1); rtsp_stream::launch_session_raise(launch_session); @@ -1950,7 +1951,7 @@ namespace nvhttp { response->close_connection_after_response = true; }); - tree.put("root.resume", 0); + tree.put("root.gamesession", 0); tree.put("root..status_code", 400); tree.put("root..status_message", "Missing a required launch parameter"); @@ -1959,7 +1960,7 @@ namespace nvhttp { auto appid = util::from_view(get_arg(args, "appid")); - launch_app(host_audio, response, request, args, appid); + launch_app(host_audio, response, request, args, appid, "gamesession"); } void @@ -2008,7 +2009,7 @@ namespace nvhttp { } g.disable(); - launch_app(host_audio, response, request, args, desktop_appid); + launch_app(host_audio, response, request, args, desktop_appid, "resume"); return; } @@ -2052,7 +2053,7 @@ namespace nvhttp { tree.put("root..status_code", 403); tree.put("root..status_message", "Encryption is mandatory for this host but unsupported by the client"); - tree.put("root.gamesession", 0); + tree.put("root.resume", 0); return; } From b1c9888498e49ccc25b6ee1ae52c34209500ee2b Mon Sep 17 00:00:00 2001 From: ShadowLemoon <119576779+ShadowLemoon@users.noreply.github.com> Date: Sat, 9 May 2026 23:48:15 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=89=B9?= =?UTF-8?q?=E6=AE=8A=E6=A1=8C=E9=9D=A2app?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/nvhttp.cpp | 32 ++------------------- src/process.cpp | 74 +++++++++++++++++++++++++++++++++++++------------ src/process.h | 6 ++++ 3 files changed, 64 insertions(+), 48 deletions(-) diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index 7365c54f59c..8acbd178b96 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -1103,6 +1103,7 @@ namespace nvhttp { tree.put("root.currentgame", current_appid); tree.put("root.state", current_appid > 0 ? "SUNSHINE_SERVER_BUSY" : "SUNSHINE_SERVER_FREE"); tree.put("root.appListEtag", proc::proc.get_apps_etag()); + tree.put("root.DesktopSpecialAppSupport", 1); // AI capability: inform client if AI proxy is available tree.put("root.AiCapability", confighttp::isAiEnabled() ? 1 : 0); @@ -1792,26 +1793,6 @@ namespace nvhttp { && args.find("rikeyid"s) != std::end(args); } - static int - find_desktop_app_id() { - const auto &apps = proc::proc.get_apps(); - auto desktop = std::find_if(std::begin(apps), std::end(apps), [](const auto &app) { - return app.image_path == "desktop" || app.name == "Desktop"; - }); - - if (desktop == std::end(apps)) { - desktop = std::find_if(std::begin(apps), std::end(apps), [](const auto &app) { - return app.cmd.empty(); - }); - } - - if (desktop == std::end(apps)) { - return 0; - } - - return util::from_view(desktop->id); - } - void launch_app(bool &host_audio, resp_https_t response, req_https_t request, const args_t &args, int appid, const char *result_node_name) { pt::ptree tree; @@ -1999,17 +1980,8 @@ namespace nvhttp { auto current_appid = proc::proc.running(); if (current_appid == 0) { - auto desktop_appid = find_desktop_app_id(); - if (desktop_appid <= 0) { - tree.put("root.resume", 0); - tree.put("root..status_code", 404); - tree.put("root..status_message", "No running app to resume and Desktop app not found"); - - return; - } - g.disable(); - launch_app(host_audio, response, request, args, desktop_appid, "resume"); + launch_app(host_audio, response, request, args, proc::DESKTOP_APP_ID, "resume"); return; } diff --git a/src/process.cpp b/src/process.cpp index 4a38b8fdceb..d41ecc8a98a 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -62,6 +62,21 @@ namespace proc { } }; + namespace { + ctx_t + make_desktop_app() { + ctx_t app; + app.name = std::string(DESKTOP_APP_NAME); + app.image_path = std::string(DESKTOP_APP_IMAGE_PATH); + app.id = std::to_string(DESKTOP_APP_ID); + app.auto_detach = true; + app.wait_all = true; + app.mouse_mode = 0; + app.exit_timeout = std::chrono::seconds { 5 }; + return app; + } + } // namespace + std::unique_ptr init() { return std::make_unique(); @@ -156,17 +171,23 @@ namespace proc { // Ensure starting from a clean slate terminate(); - auto iter = std::find_if(_apps.begin(), _apps.end(), [&app_id](const auto app) { - return app.id == std::to_string(app_id); - }); + _app_id = app_id; + if (app_id == DESKTOP_APP_ID) { + _app = make_desktop_app(); + } + else { + auto iter = std::find_if(_apps.begin(), _apps.end(), [&app_id](const auto app) { + return app.id == std::to_string(app_id); + }); - if (iter == _apps.end()) { - BOOST_LOG(error) << "Couldn't find app with ID ["sv << app_id << ']'; - return 404; + if (iter == _apps.end()) { + BOOST_LOG(error) << "Couldn't find app with ID ["sv << app_id << ']'; + return 404; + } + + _app = *iter; } - _app_id = app_id; - _app = *iter; _app_prep_begin = std::begin(_app.prep_cmds); _app_prep_it = _app_prep_begin; @@ -409,7 +430,8 @@ namespace proc { for (const auto &app : apps) { combined_info += app.id + app.name; } - + combined_info += std::to_string(DESKTOP_APP_ID) + std::string(DESKTOP_APP_NAME); + // Use CRC32 for the tag, same as used elsewhere auto crc = calculate_crc32(combined_info); _apps_etag = std::to_string(crc); @@ -441,6 +463,10 @@ namespace proc { // Returns http content-type header compatible image type. std::string proc_t::get_app_image(int app_id) { + if (app_id == DESKTOP_APP_ID) { + return validate_app_image_path(std::string(DESKTOP_APP_IMAGE_PATH)); + } + auto iter = std::find_if(_apps.begin(), _apps.end(), [&app_id](const auto app) { return app.id == std::to_string(app_id); }); @@ -451,6 +477,10 @@ namespace proc { std::string proc_t::get_app_name(int app_id) { + if (app_id == DESKTOP_APP_ID) { + return std::string(DESKTOP_APP_NAME); + } + auto iter = std::find_if(_apps.begin(), _apps.end(), [&app_id](const auto app) { return app.id == std::to_string(app_id); }); @@ -459,6 +489,10 @@ namespace proc { std::string proc_t::get_app_cmd(int app_id) { + if (app_id == DESKTOP_APP_ID) { + return std::string(); + } + auto iter = std::find_if(_apps.begin(), _apps.end(), [&app_id](const auto app) { return app.id == std::to_string(app_id); }); @@ -885,15 +919,19 @@ namespace proc { ctx.mouse_mode = mouse_mode.value_or(0); ctx.exit_timeout = std::chrono::seconds { exit_timeout.value_or(5) }; - auto possible_ids = calculate_app_id(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); - } + std::tuple possible_ids; + do { + possible_ids = calculate_app_id(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); + } + } while (ids.count(ctx.id) != 0 || ctx.id == std::to_string(DESKTOP_APP_ID)); + ids.insert(ctx.id); ctx.name = std::move(name); diff --git a/src/process.h b/src/process.h index 0b0e686b3a4..431f3cbf8df 100644 --- a/src/process.h +++ b/src/process.h @@ -8,7 +8,9 @@ #define __kernel_entry #endif +#include #include +#include #include #include @@ -21,6 +23,10 @@ namespace proc { using file_t = util::safe_ptr_v2; + inline constexpr int DESKTOP_APP_ID = std::numeric_limits::max(); + inline constexpr std::string_view DESKTOP_APP_NAME = "Desktop"; + inline constexpr std::string_view DESKTOP_APP_IMAGE_PATH = "desktop"; + typedef config::prep_cmd_t cmd_t; struct scmd_t { scmd_t(std::string &&id, std::string &&name, std::string &&do_cmd, bool &&elevated):