Fix: Web remote desktop reliability and UX
- Server: clamp web session adaptive quality to H264-only levels (>=Good) in EvaluateQuality and ApplyQualityLevel; Ultra/High (DIFF/RGB565) caused the browser to freeze ~1 min into a session - Server: move session-type detection to the top of ScreenSpyDlg::OnInitDialog and skip SetWindowPlacement/EnterFullScreen for hidden web sessions, eliminating the MFC dialog flash on web-triggered opens - Linux client: default QualityLevel from QUALITY_ADAPTIVE to QUALITY_GOOD to match Windows/macOS so the server's adaptive controller doesn't auto-upgrade to non-H264 algorithms - Web: clear the floating quick-action toolbar on fullscreen exit so its row of buttons (RDP reset / Mouse / Close) doesn't stay pinned to the top of the page - Web: route F11 to the remote in control mode instead of toggling local fullscreen - Web: route Esc to the remote in control mode via the Keyboard Lock API instead of exiting native fullscreen
This commit is contained in:
@@ -567,6 +567,26 @@ BOOL CScreenSpyDlg::OnInitDialog()
|
||||
__super::OnInitDialog();
|
||||
SetIcon(m_hIcon,FALSE);
|
||||
|
||||
// Determine session type FIRST so the window-show machinery below
|
||||
// (SetWindowPlacement with showCmd=SW_MAXIMIZE, EnterFullScreen) can be
|
||||
// skipped for hidden web sessions. Without this, the dialog briefly
|
||||
// flashes on-screen — the hide used to happen ~200 lines later, after
|
||||
// a chain of init work that the user could see in the meantime.
|
||||
bool isMfcSession = WebService().IsMfcTriggered(m_ClientID);
|
||||
if (isMfcSession) {
|
||||
// MFC-triggered: clear the flag, don't register with WebService.
|
||||
WebService().ClearMfcTriggered(m_ClientID);
|
||||
// m_bIsWebSession remains false (default)
|
||||
} else if (WebService().IsWebTriggered(m_ClientID) && WebService().GetHideWebSessions()) {
|
||||
// Web-triggered: register and hide upfront.
|
||||
WebService().RegisterScreenContext(m_ClientID, m_ContextObject);
|
||||
m_bHide = true;
|
||||
m_bIsWebSession = true;
|
||||
ShowWindow(SW_HIDE);
|
||||
}
|
||||
Mprintf("[ScreenSpy] Dialog created for device %llu, isMfcSession=%d, isWebSession=%d\n",
|
||||
m_ClientID, isMfcSession ? 1 : 0, m_bIsWebSession.load() ? 1 : 0);
|
||||
|
||||
// 获取默认 IME 上下文(ImmAssociateContext 返回之前关联的上下文)
|
||||
// 先禁用再恢复,以获取原始上下文句柄
|
||||
m_hOldIMC = ImmAssociateContext(m_hWnd, NULL);
|
||||
@@ -730,11 +750,19 @@ BOOL CScreenSpyDlg::OnInitDialog()
|
||||
wp.rcNormalPosition.right = normalX + normalWidth;
|
||||
wp.rcNormalPosition.bottom = normalY + normalHeight;
|
||||
wp.showCmd = SW_MAXIMIZE;
|
||||
SetWindowPlacement(&wp);
|
||||
|
||||
// 同时初始化 m_struOldWndpl,供全屏退出时使用
|
||||
m_struOldWndpl = wp;
|
||||
m_Settings.FullScreen ? EnterFullScreen() : LeaveFullScreen();
|
||||
// Skip the placement + fullscreen machinery for hidden web sessions —
|
||||
// those calls would briefly flash the dialog on the user's desktop
|
||||
// before the early ShowWindow(SW_HIDE) above takes effect at WM_PAINT
|
||||
// time. m_struOldWndpl still gets a sane snapshot so any defensive
|
||||
// LeaveFullScreen path later has something to restore against.
|
||||
if (!m_bIsWebSession) {
|
||||
SetWindowPlacement(&wp);
|
||||
m_struOldWndpl = wp;
|
||||
m_Settings.FullScreen ? EnterFullScreen() : LeaveFullScreen();
|
||||
} else {
|
||||
m_struOldWndpl = wp;
|
||||
}
|
||||
|
||||
// 启动传输速率更新定时器 (1秒)
|
||||
SetTimer(4, 1000, NULL);
|
||||
@@ -771,32 +799,9 @@ BOOL CScreenSpyDlg::OnInitDialog()
|
||||
if (pMain)
|
||||
::PostMessage(pMain->GetSafeHwnd(), WM_SESSION_ACTIVATED, (WPARAM)this, 0);
|
||||
|
||||
// Determine session type: MFC or Web
|
||||
// Must check MfcTriggered FIRST - if MFC triggered this dialog, it's NOT a web session
|
||||
// even if WebTriggered is also true (happens when Web is already open for same device)
|
||||
bool isMfcSession = WebService().IsMfcTriggered(m_ClientID);
|
||||
bool isWebSession = false;
|
||||
if (isMfcSession) {
|
||||
// MFC session: clear the flag, don't register with WebService
|
||||
WebService().ClearMfcTriggered(m_ClientID);
|
||||
// m_bIsWebSession remains false (default)
|
||||
} else {
|
||||
// Check if this is a Web session
|
||||
isWebSession = WebService().IsWebTriggered(m_ClientID) && WebService().GetHideWebSessions();
|
||||
|
||||
// Only register screen context for Web sessions
|
||||
// MFC dialogs handle input directly via m_ContextObject, don't need WebService registry
|
||||
// This prevents MFC close from deleting Web's context (they share same device_id key)
|
||||
if (isWebSession) {
|
||||
WebService().RegisterScreenContext(m_ClientID, m_ContextObject);
|
||||
m_bHide = true;
|
||||
m_bIsWebSession = true;
|
||||
ShowWindow(SW_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
Mprintf("[ScreenSpy] Dialog created for device %llu, isMfcSession=%d, isWebSession=%d\n",
|
||||
m_ClientID, isMfcSession ? 1 : 0, isWebSession ? 1 : 0);
|
||||
// Session type detection and visibility moved to the top of this function
|
||||
// (right after __super::OnInitDialog) so SetWindowPlacement / EnterFullScreen
|
||||
// above can be skipped for web sessions and avoid a visible flash.
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -2277,6 +2282,16 @@ void CScreenSpyDlg::EvaluateQuality()
|
||||
|
||||
// 2. 计算目标等级
|
||||
int targetLevel = GetTargetQualityLevel(rtt, m_bUsingFRP);
|
||||
|
||||
// Web 会话只解码 H264。levels 0 (Ultra/DIFF) 和 1 (High/RGB565) 在网络好的时候
|
||||
// 会被自适应控制器选中,但 device 切到那两个算法后浏览器的 WebCodecs decoder
|
||||
// 就吃不下了,画面冻在最后一个 H264 帧上 (用户感受是 ~1 分钟后卡死,
|
||||
// 因为 EvaluateQuality 启动 1 分钟才开始评估)。Clamp 到最高质量的 H264 等级
|
||||
// 让自适应在 H264 区间内继续工作.
|
||||
if (m_bIsWebSession && targetLevel < QUALITY_GOOD) {
|
||||
targetLevel = QUALITY_GOOD;
|
||||
}
|
||||
|
||||
int currentLevel = m_AdaptiveQuality.currentLevel;
|
||||
|
||||
if (targetLevel == currentLevel) {
|
||||
@@ -2322,6 +2337,14 @@ void CScreenSpyDlg::ApplyQualityLevel(int level, bool persist)
|
||||
{
|
||||
if (level < 0 || level >= QUALITY_COUNT) return;
|
||||
|
||||
// Defense in depth: ALL paths that set quality level (adaptive controller,
|
||||
// manual menu pick, restore from config, …) flow through here. Web
|
||||
// sessions cannot use non-H264 levels — see EvaluateQuality for the
|
||||
// failure mode explanation.
|
||||
if (m_bIsWebSession && level < QUALITY_GOOD) {
|
||||
level = QUALITY_GOOD;
|
||||
}
|
||||
|
||||
const QualityProfile& profile = GetQualityProfile(level);
|
||||
int oldMaxWidth = m_AdaptiveQuality.currentMaxWidth;
|
||||
int newMaxWidth = profile.maxWidth;
|
||||
|
||||
Reference in New Issue
Block a user