Server: implement per-device display mode override
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user