Skip to content

Commit 0c379ba

Browse files
authored
feat: 消除TLS队列开销、优化托盘状态显示、修复屏幕快捷键切换、修复副屏设置 (#546)
* refactor(nvhttp):消除队列开销,用 shared_mutex 替换 mutex 以实现证书链管理 * feat: 优化托盘状态显示 * feat: 安全退出程序 * feat: 调整VDD设备创建和拓扑检查逻辑,优化延迟和超时设置 * fix: 修复屏幕快捷键切换 * fix: 修复ReadFile调用中的缓冲区处理,确保正确读取响应
1 parent 245fa70 commit 0c379ba

9 files changed

Lines changed: 174 additions & 109 deletions

File tree

src/display_device/session.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ namespace display_device {
435435

436436
BOOST_LOG(info) << "重新加载VDD驱动...";
437437
vdd_utils::reload_driver();
438-
std::this_thread::sleep_for(1500ms);
438+
std::this_thread::sleep_for(1200ms);
439439
}
440440

441441
void
@@ -446,6 +446,10 @@ namespace display_device {
446446

447447
auto device_zako = display_device::find_device_by_friendlyname(ZAKO_NAME);
448448

449+
// pre_vdd_devices: 在 VDD 创建前一刻保存的物理显示器快照
450+
// 延迟到 VDD 创建前才捕获,确保无论是新建还是重建都能拿到正确状态
451+
device_info_map_t pre_vdd_devices;
452+
449453
// Rebuild VDD device on client switch
450454
if (!device_zako.empty() && !current_vdd_client_id.empty() &&
451455
!current_client_id.empty() && current_vdd_client_id != current_client_id) {
@@ -492,13 +496,18 @@ namespace display_device {
492496

493497
// Create VDD device if not present
494498
if (device_zako.empty()) {
499+
// 在创建 VDD 之前捕获物理显示器快照
500+
// 此时无 VDD 存在(新建 or 重建后已销毁),物理屏应处于正常状态
501+
pre_vdd_devices = display_device::enum_available_devices();
502+
BOOST_LOG(info) << "已保存pre-VDD设备列表: " << display_device::to_string(pre_vdd_devices);
503+
495504
BOOST_LOG(info) << "创建虚拟显示器...";
496505
// 复用模式使用固定标识符,否则使用客户端ID生成唯一GUID
497506
const std::string vdd_identifier = config::video.vdd_reuse
498507
? "shared_vdd" // 固定标识符,所有客户端共用同一GUID
499508
: current_client_id; // 为每个客户端生成不同GUID
500509
vdd_utils::create_vdd_monitor(vdd_identifier, hdr_brightness, physical_size);
501-
std::this_thread::sleep_for(500ms);
510+
std::this_thread::sleep_for(200ms);
502511
}
503512

504513
// Wait for device to be ready
@@ -542,9 +551,9 @@ namespace display_device {
542551
// VDD模式下的拓扑控制与普通模式分开处理
543552
if (config.vdd_prep != parsed_config_t::vdd_prep_e::no_operation) {
544553
// User has specified a display configuration, apply it
545-
if (vdd_utils::apply_vdd_prep(device_zako, config.vdd_prep)) {
554+
if (vdd_utils::apply_vdd_prep(device_zako, config.vdd_prep, pre_vdd_devices)) {
546555
BOOST_LOG(info) << "已应用VDD屏幕布局设置";
547-
std::this_thread::sleep_for(500ms);
556+
std::this_thread::sleep_for(200ms);
548557
}
549558
}
550559
else {

src/display_device/vdd_utils.cpp

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ namespace display_device {
3333
namespace vdd_utils {
3434

3535
const wchar_t *kVddPipeName = L"\\\\.\\pipe\\ZakoVDDPipe";
36-
const DWORD kPipeTimeoutMs = 5000;
36+
const DWORD kPipeTimeoutMs = 3000;
3737
const DWORD kPipeBufferSize = 4096;
3838
const std::chrono::milliseconds kDefaultDebounceInterval { 2000 };
3939

@@ -141,7 +141,7 @@ namespace display_device {
141141
}
142142

143143
bool
144-
execute_pipe_command(const wchar_t *pipe_name, const wchar_t *command, std::string *response) {
144+
execute_pipe_command(const wchar_t *pipe_name, const wchar_t *command, std::string *response, bool *timed_out) {
145145
auto hPipe = connect_to_pipe_with_retry(pipe_name);
146146
if (hPipe == INVALID_HANDLE_VALUE) {
147147
BOOST_LOG(error) << "连接MTT虚拟显示管道失败,已重试多次";
@@ -180,10 +180,11 @@ namespace display_device {
180180
}
181181

182182
// 读取响应
183+
bool read_timed_out = false;
183184
if (response) {
184185
char buffer[kPipeBufferSize];
185-
DWORD bytesRead;
186-
if (!ReadFile(hPipe, buffer, sizeof(buffer), &bytesRead, &overlapped)) {
186+
DWORD bytesRead = 0;
187+
if (!ReadFile(hPipe, buffer, sizeof(buffer) - 1, &bytesRead, &overlapped)) {
187188
if (GetLastError() != ERROR_IO_PENDING) {
188189
BOOST_LOG(warning) << "读取响应失败,错误代码: " << GetLastError();
189190
return false;
@@ -194,9 +195,21 @@ namespace display_device {
194195
buffer[bytesRead] = '\0';
195196
*response = std::string(buffer, bytesRead);
196197
}
198+
else {
199+
read_timed_out = true;
200+
CancelIo(hPipe);
201+
}
202+
}
203+
else {
204+
// ReadFile completed synchronously
205+
buffer[bytesRead] = '\0';
206+
*response = std::string(buffer, bytesRead);
197207
}
198208
}
199209

210+
if (timed_out) {
211+
*timed_out = read_timed_out;
212+
}
200213
return true;
201214
}
202215

@@ -309,12 +322,14 @@ namespace display_device {
309322
}
310323

311324
// 尝试发送命令(带GUID或不带GUID)
312-
bool success = execute_pipe_command(kVddPipeName, command.c_str(), &response);
325+
bool read_timed_out = false;
326+
bool success = execute_pipe_command(kVddPipeName, command.c_str(), &response, &read_timed_out);
313327

314328
// 如果带GUID的命令失败,降级为不带GUID的命令(兼容旧版驱动)
315329
if (!success && !guid_str.empty()) {
316330
BOOST_LOG(warning) << "带GUID的命令失败,尝试降级为不带GUID的命令";
317-
success = execute_pipe_command(kVddPipeName, L"CREATEMONITOR", &response);
331+
read_timed_out = false;
332+
success = execute_pipe_command(kVddPipeName, L"CREATEMONITOR", &response, &read_timed_out);
318333
}
319334

320335
if (!success) {
@@ -325,7 +340,7 @@ namespace display_device {
325340
#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1
326341
system_tray::update_vdd_menu();
327342
#endif
328-
BOOST_LOG(info) << "创建虚拟显示器完成,响应: " << response;
343+
BOOST_LOG(info) << "创建虚拟显示器完成,响应: " << response << " [return=" << (read_timed_out ? 1 : 0) << "]";
329344
return true;
330345
}
331346

@@ -627,7 +642,7 @@ namespace display_device {
627642
set_hdr_state(bool enable_hdr) {
628643
auto vdd_device_id = find_device_by_friendlyname(ZAKO_NAME);
629644
if (vdd_device_id.empty()) {
630-
BOOST_LOG(debug) << "未找到虚拟显示器设备,跳过HDR状态设置";
645+
BOOST_LOG(info) << "未找到虚拟显示器设备,跳过HDR状态设置";
631646
return true;
632647
}
633648

@@ -636,13 +651,13 @@ namespace display_device {
636651

637652
auto hdr_state_it = current_hdr_states.find(vdd_device_id);
638653
if (hdr_state_it == current_hdr_states.end()) {
639-
BOOST_LOG(debug) << "虚拟显示器不支持HDR或状态未知";
654+
BOOST_LOG(info) << "虚拟显示器不支持HDR或状态未知";
640655
return true;
641656
}
642657

643658
hdr_state_e target_state = enable_hdr ? hdr_state_e::enabled : hdr_state_e::disabled;
644659
if (hdr_state_it->second == target_state) {
645-
BOOST_LOG(debug) << "虚拟显示器HDR状态已是目标状态";
660+
BOOST_LOG(info) << "虚拟显示器HDR状态已是目标状态";
646661
return true;
647662
}
648663

@@ -662,33 +677,58 @@ namespace display_device {
662677
}
663678

664679
bool
665-
apply_vdd_prep(const std::string &vdd_device_id, parsed_config_t::vdd_prep_e vdd_prep) {
680+
apply_vdd_prep(const std::string &vdd_device_id, parsed_config_t::vdd_prep_e vdd_prep,
681+
const device_info_map_t &pre_vdd_devices) {
666682
if (vdd_device_id.empty()) {
667-
BOOST_LOG(debug) << "VDD设备ID为空,跳过vdd_prep处理";
683+
BOOST_LOG(info) << "VDD设备ID为空,跳过vdd_prep处理";
668684
return true;
669685
}
670686

671687
if (vdd_prep == parsed_config_t::vdd_prep_e::no_operation) {
672-
BOOST_LOG(debug) << "vdd_prep设置为无操作,跳过物理显示器处理";
688+
BOOST_LOG(info) << "vdd_prep设置为无操作,跳过物理显示器处理";
673689
return true;
674690
}
675691

676-
auto current_topology = get_current_topology();
677-
if (current_topology.empty()) {
678-
BOOST_LOG(warning) << "无法获取当前显示器拓扑";
679-
return false;
680-
}
681-
682-
// 找出所有物理显示器(非VDD设备)
692+
// 从 pre_vdd_devices(VDD创建前保存的设备列表)中获取物理显示器,
693+
// 确保即使 VDD 创建后物理屏变 inactive 也能正确识别
683694
std::vector<std::string> physical_devices;
684-
for (const auto &group : current_topology) {
685-
for (const auto &id : group) {
686-
if (id != vdd_device_id) {
687-
physical_devices.push_back(id);
695+
std::string original_primary_id;
696+
697+
if (!pre_vdd_devices.empty()) {
698+
// 使用 VDD 创建前保存的设备信息(可靠)
699+
for (const auto &[device_id, info] : pre_vdd_devices) {
700+
if (info.friendly_name != ZAKO_NAME) {
701+
physical_devices.push_back(device_id);
702+
if (info.device_state == device_state_e::primary) {
703+
original_primary_id = device_id;
704+
}
705+
}
706+
}
707+
BOOST_LOG(info) << "使用pre-VDD设备列表: " << physical_devices.size() << "个物理显示器"
708+
<< (original_primary_id.empty() ? "" : ", 原主屏: " + original_primary_id);
709+
}
710+
else {
711+
// 回退:从当前设备枚举中获取(VDD创建前未保存时的兜底逻辑)
712+
BOOST_LOG(warning) << "未提供pre-VDD设备列表,从当前设备枚举中查找物理显示器";
713+
const auto all_devices = enum_available_devices();
714+
for (const auto &[device_id, info] : all_devices) {
715+
if (device_id != vdd_device_id && info.friendly_name != ZAKO_NAME) {
716+
physical_devices.push_back(device_id);
717+
if (info.device_state == device_state_e::primary) {
718+
original_primary_id = device_id;
719+
}
688720
}
689721
}
690722
}
691723

724+
// 确保原主屏在列表最前面(set_topology 中第一组拥有主屏优先权)
725+
if (!original_primary_id.empty()) {
726+
auto it = std::find(physical_devices.begin(), physical_devices.end(), original_primary_id);
727+
if (it != physical_devices.begin() && it != physical_devices.end()) {
728+
std::rotate(physical_devices.begin(), it, it + 1);
729+
}
730+
}
731+
692732
if (physical_devices.empty()) {
693733
BOOST_LOG(debug) << "没有物理显示器需要处理";
694734
return true;

src/display_device/vdd_utils.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace display_device::vdd_utils {
1919
// 常量定义
2020
inline constexpr int kMaxRetryCount = 3;
2121
inline constexpr auto kInitialRetryDelay = 500ms;
22-
inline constexpr auto kMaxRetryDelay = 5000ms;
22+
inline constexpr auto kMaxRetryDelay = 3000ms;
2323

2424
extern const wchar_t *kVddPipeName;
2525
extern const DWORD kPipeTimeoutMs;
@@ -67,7 +67,7 @@ namespace display_device::vdd_utils {
6767
connect_to_pipe_with_retry(const wchar_t *pipe_name, int max_retries = 3);
6868

6969
bool
70-
execute_pipe_command(const wchar_t *pipe_name, const wchar_t *command, std::string *response = nullptr);
70+
execute_pipe_command(const wchar_t *pipe_name, const wchar_t *command, std::string *response = nullptr, bool *timed_out = nullptr);
7171

7272
// 驱动重载函数
7373
bool
@@ -134,12 +134,16 @@ namespace display_device::vdd_utils {
134134
* @brief Apply VDD prep settings to handle physical displays.
135135
* @param vdd_device_id The VDD device ID.
136136
* @param vdd_prep The vdd_prep_e value specifying how to handle physical displays.
137+
* @param pre_vdd_devices Physical device info captured BEFORE VDD creation.
138+
* Used to reliably identify physical displays even if VDD creation
139+
* caused them to become inactive. If empty, falls back to current device enumeration.
137140
* @returns True if the operation succeeded.
138141
* @note This operation modifies topology without saving/restoring state,
139142
* as Windows automatically handles topology memory when displays change.
140143
*/
141144
bool
142-
apply_vdd_prep(const std::string &vdd_device_id, parsed_config_t::vdd_prep_e vdd_prep);
145+
apply_vdd_prep(const std::string &vdd_device_id, parsed_config_t::vdd_prep_e vdd_prep,
146+
const device_info_map_t &pre_vdd_devices = {});
143147

144148
VddSettings
145149
prepare_vdd_settings(const parsed_config_t &config);

src/entry_handler.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,7 @@ namespace lifetime {
8888
int zero = 0;
8989
desired_exit_code.compare_exchange_strong(zero, exit_code);
9090

91-
// Raise SIGINT to start termination
92-
std::raise(SIGINT);
91+
mail::man->event<bool>(mail::shutdown)->raise(true);
9392

9493
// Termination will happen asynchronously, but the caller may
9594
// have wanted synchronous behavior.

src/main.cpp

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ main(int argc, char *argv[]) {
306306

307307
// Create signal handler after logging has been initialized
308308
auto shutdown_event = mail::man->event<bool>(mail::shutdown);
309-
on_signal(SIGINT, [&force_shutdown, &display_device_deinit_guard, shutdown_event]() {
309+
on_signal(SIGINT, [&force_shutdown, shutdown_event]() {
310310
BOOST_LOG(info) << "Interrupt handler called"sv;
311311

312312
auto task = []() {
@@ -316,19 +316,10 @@ main(int argc, char *argv[]) {
316316
};
317317
force_shutdown = task_pool.pushDelayed(task, 10s).task_id;
318318

319-
// Break out of the main loop
320319
shutdown_event->raise(true);
321-
system_tray::end_tray();
322-
try {
323-
display_device::session_t::get().restore_state();
324-
}
325-
catch (...) {
326-
}
327-
328-
display_device_deinit_guard = nullptr;
329320
});
330321

331-
on_signal(SIGTERM, [&force_shutdown, &display_device_deinit_guard, shutdown_event]() {
322+
on_signal(SIGTERM, [&force_shutdown, shutdown_event]() {
332323
BOOST_LOG(info) << "Terminate handler called"sv;
333324

334325
auto task = []() {
@@ -338,16 +329,7 @@ main(int argc, char *argv[]) {
338329
};
339330
force_shutdown = task_pool.pushDelayed(task, 10s).task_id;
340331

341-
// Break out of the main loop
342332
shutdown_event->raise(true);
343-
system_tray::end_tray();
344-
try {
345-
display_device::session_t::get().restore_state();
346-
}
347-
catch (...) {
348-
}
349-
350-
display_device_deinit_guard = nullptr;
351333
});
352334

353335
#ifdef _WIN32
@@ -441,6 +423,14 @@ main(int argc, char *argv[]) {
441423

442424
mainThreadLoop(shutdown_event);
443425

426+
system_tray::end_tray();
427+
try {
428+
display_device::session_t::get().restore_state();
429+
}
430+
catch (...) {
431+
}
432+
display_device_deinit_guard = nullptr;
433+
444434
httpThread.join();
445435
configThread.join();
446436
rtspThread.join();

0 commit comments

Comments
 (0)