diff --git a/src/confighttp.cpp b/src/confighttp.cpp index 09b604a7..d42e4c29 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -700,6 +700,7 @@ namespace confighttp { * { * "uuid": "", * "name": "", + * "display_mode": "1920x1080x59.94", * "do": [ { "cmd": "", "elevated": false }, ... ], * "undo": [ { "cmd": "", "elevated": false }, ... ], * "perm": @@ -720,10 +721,11 @@ namespace confighttp { nlohmann::json output_tree; std::string uuid = input_tree.value("uuid", ""); std::string name = input_tree.value("name", ""); + std::string display_mode = input_tree.value("display_mode", ""); auto do_cmds = nvhttp::extract_command_entries(input_tree, "do"); auto undo_cmds = nvhttp::extract_command_entries(input_tree, "undo"); auto perm = static_cast(input_tree.value("perm", static_cast(crypto::PERM::_no)) & static_cast(crypto::PERM::_all)); - output_tree["status"] = nvhttp::update_device_info(uuid, name, do_cmds, undo_cmds, perm); + output_tree["status"] = nvhttp::update_device_info(uuid, name, display_mode, do_cmds, undo_cmds, perm); send_response(response, output_tree); } catch (std::exception &e) { BOOST_LOG(warning) << "Update Client: "sv << e.what(); diff --git a/src/crypto.h b/src/crypto.h index 4abed125..c854f737 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -100,6 +100,7 @@ namespace crypto { std::string name; std::string uuid; std::string cert; + std::string display_mode; std::list do_cmds; std::list undo_cmds; PERM perm; diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index d3e7bc23..1fb63205 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -245,6 +245,7 @@ namespace nvhttp { named_cert_node["name"] = final_name; named_cert_node["cert"] = named_cert_p->cert; named_cert_node["uuid"] = named_cert_p->uuid; + named_cert_node["display_mode"] = named_cert_p->display_mode; named_cert_node["perm"] = static_cast(named_cert_p->perm); // Add "do" commands if available. @@ -320,6 +321,7 @@ namespace nvhttp { named_cert_p->name = ""; named_cert_p->cert = el.get(); named_cert_p->uuid = uuid_util::uuid_t::generate().string(); + named_cert_p->display_mode = ""; named_cert_p->perm = PERM::_all; client.named_devices.emplace_back(named_cert_p); } @@ -334,6 +336,7 @@ namespace nvhttp { named_cert_p->name = el.value("name", ""); named_cert_p->cert = el.value("cert", ""); named_cert_p->uuid = el.value("uuid", ""); + named_cert_p->display_mode = el.value("display_mode", ""); named_cert_p->perm = (PERM)(util::get_non_string_json_value(el, "perm", (uint32_t)PERM::_all)) & PERM::_all; // Load command entries for "do" and "undo" keys. named_cert_p->do_cmds = extract_command_entries(el, "do"); @@ -399,7 +402,16 @@ namespace nvhttp { std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session->iv)); } - std::stringstream mode = std::stringstream(get_arg(args, "mode", config::video.fallback_mode.c_str())); + std::stringstream mode; + if (named_cert_p->display_mode.empty()) { + auto mode_str = get_arg(args, "mode", config::video.fallback_mode.c_str()); + mode = std::stringstream(mode_str); + BOOST_LOG(info) << "Display mode for client ["sv << named_cert_p->name <<"] requested to ["sv << mode_str << ']'; + } else { + mode = std::stringstream(named_cert_p->display_mode); + BOOST_LOG(info) << "Display mode for client ["sv << named_cert_p->name <<"] overriden to ["sv << named_cert_p->display_mode << ']'; + } + // Split mode by the char "x", to populate width/height/fps int x = 0; std::string segment; @@ -411,11 +423,24 @@ namespace nvhttp { launch_session->height = atoi(segment.c_str()); } if (x == 2) { - launch_session->fps = atoi(segment.c_str()); + auto fps = atof(segment.c_str()); + launch_session->fps = (int)(fps * 1000); + break; } x++; } + // Parsing have failed or missing components + if (x != 2) { + launch_session->width = 1920; + launch_session->height = 1080; + launch_session->fps = 60000; // 60fps * 1000 denominator + } + + if (launch_session->fps < 1000) { + launch_session->fps *= 1000; + } + launch_session->device_name = named_cert_p->name.empty() ? "ApolloDisplay"s : named_cert_p->name; launch_session->unique_id = named_cert_p->uuid; launch_session->perm = named_cert_p->perm; @@ -981,6 +1006,7 @@ namespace nvhttp { nlohmann::json named_cert_node; named_cert_node["name"] = named_cert->name; named_cert_node["uuid"] = named_cert->uuid; + named_cert_node["display_mode"] = named_cert->display_mode; named_cert_node["perm"] = static_cast(named_cert->perm); // Add "do" commands if available @@ -1724,6 +1750,7 @@ namespace nvhttp { bool update_device_info( const std::string& uuid, const std::string& name, + const std::string& display_mode, const cmd_list_t& do_cmds, const cmd_list_t& undo_cmds, const crypto::PERM newPerm @@ -1736,6 +1763,7 @@ namespace nvhttp { auto named_cert_p = *it; if (named_cert_p->uuid == uuid) { named_cert_p->name = name; + named_cert_p->display_mode = display_mode; named_cert_p->perm = newPerm; named_cert_p->do_cmds = do_cmds; named_cert_p->undo_cmds = undo_cmds; diff --git a/src/nvhttp.h b/src/nvhttp.h index 0a6bd4da..d3574d86 100644 --- a/src/nvhttp.h +++ b/src/nvhttp.h @@ -271,6 +271,7 @@ namespace nvhttp { bool update_device_info( const std::string& uuid, const std::string& name, + const std::string& display_mode, const cmd_list_t& do_cmds, const cmd_list_t& undo_cmds, const crypto::PERM newPerm diff --git a/src/platform/windows/virtual_display.cpp b/src/platform/windows/virtual_display.cpp index 5c5a41d3..94af232f 100644 --- a/src/platform/windows/virtual_display.cpp +++ b/src/platform/windows/virtual_display.cpp @@ -27,21 +27,75 @@ LONG getDeviceSettings(const wchar_t* deviceName, DEVMODEW& devMode) { } LONG changeDisplaySettings(const wchar_t* deviceName, int width, int height, int refresh_rate) { - DEVMODEW devMode = {0}; - devMode.dmSize = sizeof(devMode); - - if (EnumDisplaySettingsW(deviceName, ENUM_CURRENT_SETTINGS, &devMode)) { - devMode.dmPelsWidth = width; - devMode.dmPelsHeight = height; - devMode.dmDisplayFrequency = refresh_rate; - devMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; - - return ChangeDisplaySettingsExW(deviceName, &devMode, NULL, CDS_UPDATEREGISTRY, NULL); + UINT32 pathCount = 0; + UINT32 modeCount = 0; + if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount)) { + wprintf(L"[SUDOVDA] Failed to query display configuration size.\n"); + return ERROR_INVALID_PARAMETER; } - return 0; + std::vector pathArray(pathCount); + std::vector modeArray(modeCount); + + if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, pathArray.data(), &modeCount, modeArray.data(), nullptr) != ERROR_SUCCESS) { + wprintf(L"[SUDOVDA] Failed to query display configuration.\n"); + return ERROR_INVALID_PARAMETER; + } + + for (UINT32 i = 0; i < pathCount; i++) { + DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName = {}; + sourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; + sourceName.header.size = sizeof(sourceName); + sourceName.header.adapterId = pathArray[i].sourceInfo.adapterId; + sourceName.header.id = pathArray[i].sourceInfo.id; + + if (DisplayConfigGetDeviceInfo(&sourceName.header) != ERROR_SUCCESS) { + continue; + } + + auto* sourceInfo = &pathArray[i].sourceInfo; + auto* targetInfo = &pathArray[i].targetInfo; + + if (std::wstring_view(sourceName.viewGdiDeviceName) == std::wstring_view(deviceName)) { + wprintf(L"[SUDOVDA] Display found: %ls\n", deviceName); + for (UINT32 j = 0; j < modeCount; j++) { + if ( + modeArray[j].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE && + modeArray[j].adapterId.HighPart == sourceInfo->adapterId.HighPart && + modeArray[j].adapterId.LowPart == sourceInfo->adapterId.LowPart && + modeArray[j].id == sourceInfo->id + ) { + auto* sourceMode = &modeArray[j].sourceMode; + + wprintf(L"[SUDOVDA] Current mode found: %dx%dx%d\n", sourceMode->width, sourceMode->height, targetInfo->refreshRate); + + sourceMode->width = width; + sourceMode->height = height; + + targetInfo->refreshRate = {(UINT32)refresh_rate, 1000}; + + // Apply the changes + LONG status = SetDisplayConfig(pathCount, pathArray.data(), modeCount, modeArray.data(), SDC_APPLY | SDC_USE_SUPPLIED_DISPLAY_CONFIG | SDC_SAVE_TO_DATABASE | SDC_ALLOW_CHANGES); + if (status != ERROR_SUCCESS) { + wprintf(L"[SUDOVDA] Failed to apply display settings.\n"); + } else { + wprintf(L"[SUDOVDA] Display settings updated successfully.\n"); + } + + return status; + } + } + + wprintf(L"[SUDOVDA] Mode %dx%dx%d not found for display: %ls\n", width, height, refresh_rate, deviceName); + return ERROR_INVALID_PARAMETER; + } + } + + wprintf(L"[SUDOVDA] Display not found: %ls\n", deviceName); + return ERROR_DEVICE_NOT_CONNECTED; } + std::wstring getPrimaryDisplay() { DISPLAY_DEVICEW displayDevice; displayDevice.cb = sizeof(DISPLAY_DEVICE); @@ -117,8 +171,8 @@ bool setPrimaryDisplay(const wchar_t* primaryDeviceName) { } bool findDisplayIds(const wchar_t* displayName, LUID& adapterId, uint32_t& targetId) { - UINT pathCount; - UINT modeCount; + UINT32 pathCount; + UINT32 modeCount; if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount)) { return false; } @@ -132,17 +186,17 @@ bool findDisplayIds(const wchar_t* displayName, LUID& adapterId, uint32_t& targe auto path = std::find_if(paths.begin(), paths.end(), [&displayName](DISPLAYCONFIG_PATH_INFO _path) { DISPLAYCONFIG_PATH_SOURCE_INFO sourceInfo = _path.sourceInfo; - DISPLAYCONFIG_SOURCE_DEVICE_NAME deviceName = {}; - deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; - deviceName.header.size = sizeof(deviceName); - deviceName.header.adapterId = sourceInfo.adapterId; - deviceName.header.id = sourceInfo.id; + DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName = {}; + sourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; + sourceName.header.size = sizeof(sourceName); + sourceName.header.adapterId = sourceInfo.adapterId; + sourceName.header.id = sourceInfo.id; - if (DisplayConfigGetDeviceInfo(&deviceName.header) != ERROR_SUCCESS) { + if (DisplayConfigGetDeviceInfo(&sourceName.header) != ERROR_SUCCESS) { return false; } - return std::wstring_view(displayName) == deviceName.viewGdiDeviceName; + return std::wstring_view(displayName) == sourceName.viewGdiDeviceName; }); if (path == paths.end()) { diff --git a/src/process.cpp b/src/process.cpp index c11a8e3c..f23c61b0 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -261,7 +261,11 @@ namespace proc { memcpy(&launch_session->display_guid, &device_uuid, sizeof(GUID)); - int target_fps = launch_session->fps ? launch_session->fps : 60; + int target_fps = launch_session->fps ? launch_session->fps : 60000; + + if (target_fps < 1000) { + target_fps *= 1000; + } std::wstring vdisplayName = VDISPLAY::createVirtualDisplay( device_uuid_str.c_str(), diff --git a/src/rtsp.cpp b/src/rtsp.cpp index 7fa0f526..044de62a 100644 --- a/src/rtsp.cpp +++ b/src/rtsp.cpp @@ -1005,7 +1005,7 @@ namespace rtsp_stream { config.monitor.enableIntraRefresh = util::from_view(args.at("x-ss-video[0].intraRefresh"sv)); if (config::video.limit_framerate) { - config.monitor.encodingFramerate = session.fps; + config.monitor.encodingFramerate = (int)ceil(session.fps / 1000); } else { config.monitor.encodingFramerate = config.monitor.framerate; }