Server: implement per-device display mode override

This commit is contained in:
Yukino Song
2025-02-25 05:11:59 +08:00
parent 1cdd309cfd
commit c6127ed0e5
7 changed files with 115 additions and 25 deletions

View File

@@ -700,6 +700,7 @@ namespace confighttp {
* {
* "uuid": "<uuid>",
* "name": "<Friendly Name>",
* "display_mode": "1920x1080x59.94",
* "do": [ { "cmd": "<command>", "elevated": false }, ... ],
* "undo": [ { "cmd": "<command>", "elevated": false }, ... ],
* "perm": <uint32_t>
@@ -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<crypto::PERM>(input_tree.value("perm", static_cast<uint32_t>(crypto::PERM::_no)) & static_cast<uint32_t>(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();

View File

@@ -100,6 +100,7 @@ namespace crypto {
std::string name;
std::string uuid;
std::string cert;
std::string display_mode;
std::list<command_entry_t> do_cmds;
std::list<command_entry_t> undo_cmds;
PERM perm;

View File

@@ -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<uint32_t>(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<std::string>();
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<uint32_t>(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<uint32_t>(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;

View File

@@ -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

View File

@@ -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<DISPLAYCONFIG_PATH_INFO> pathArray(pathCount);
std::vector<DISPLAYCONFIG_MODE_INFO> 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()) {

View File

@@ -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(),

View File

@@ -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;
}