Fix: Ensure MFC and Web remote desktop sessions are fully independent

This commit is contained in:
yuanyuanxiang
2026-05-02 13:56:08 +02:00
parent 171fa750e5
commit 9ae5529458
8 changed files with 163 additions and 45 deletions

View File

@@ -651,11 +651,21 @@ public:
// Double-check after acquiring lock // Double-check after acquiring lock
if (m_destroyed) return; if (m_destroyed) return;
// Prevent starting if thread is already running or joinable // If already running, just send TOKEN_BITMAPINFO again
if (m_captureThread.joinable()) return; // This allows server to create additional dialogs (MFC can open while Web is active)
if (m_captureThread.joinable() || m_running.load()) {
Mprintf(">>> ScreenHandler already running, sending TOKEN_BITMAPINFO for new dialog\n");
SendBitmapInfo();
return;
}
bool expected = false; bool expected = false;
if (!m_running.compare_exchange_strong(expected, true)) return; if (!m_running.compare_exchange_strong(expected, true)) {
// Race condition: another thread started first, send bitmap info
Mprintf(">>> ScreenHandler race, sending TOKEN_BITMAPINFO for new dialog\n");
SendBitmapInfo();
return;
}
m_captureThread = std::thread(&ScreenHandler::CaptureLoop, this); m_captureThread = std::thread(&ScreenHandler::CaptureLoop, this);
} }

View File

@@ -156,7 +156,13 @@ bool ScreenHandler::init()
void ScreenHandler::start(IOCPClient* client, uint64_t clientID) void ScreenHandler::start(IOCPClient* client, uint64_t clientID)
{ {
if (m_running) return; // If already running, just send TOKEN_BITMAPINFO again
// This allows server to create additional dialogs (MFC can open while Web is active)
if (m_running) {
NSLog(@"ScreenHandler already running, sending TOKEN_BITMAPINFO for new dialog");
sendBitmapInfo();
return;
}
m_client = client; m_client = client;
m_clientID = clientID; m_clientID = clientID;

View File

@@ -3872,6 +3872,9 @@ BOOL CMy2015RemoteDlg::ShouldRemoteControl()
void screenParamModifier(context* ctx, void* user) void screenParamModifier(context* ctx, void* user)
{ {
// Mark as MFC-triggered so dialog will be visible
WebService().SetMfcTriggered(ctx->GetClientID());
auto version = ctx->GetClientData(ONLINELIST_VERSION); auto version = ctx->GetClientData(ONLINELIST_VERSION);
if (!IsDateGreaterOrEqual(version, "Feb 8 2026")) { if (!IsDateGreaterOrEqual(version, "Feb 8 2026")) {
char* param = (char*)user; char* param = (char*)user;
@@ -6350,9 +6353,18 @@ LRESULT CMy2015RemoteDlg::OnOpenScreenSpyDialog(WPARAM wParam, LPARAM lParam)
BYTE bToken = COMMAND_BYE; BYTE bToken = COMMAND_BYE;
return ContextObject->Send2Client(&bToken, 1) ? 0 : 0x20260223; return ContextObject->Send2Client(&bToken, 1) ? 0 : 0x20260223;
} }
if (clientID && WebService().IsRunning() && WebService().IsWebTriggered(clientID) && WebService().GetHideWebSessions()) { // Check trigger source: MFC-triggered dialogs are always visible
// Note: Don't clear MfcTriggered here - let OnInitDialog check it to determine session type
if (clientID && WebService().IsRunning()) {
if (WebService().IsMfcTriggered(clientID)) {
// MFC-triggered: show dialog (flag will be cleared in OnInitDialog)
return OpenDialog<CScreenSpyDlg, IDD_DIALOG_SCREEN_SPY, SW_SHOWMAXIMIZED>(wParam, lParam);
}
if (WebService().IsWebTriggered(clientID) && WebService().GetHideWebSessions()) {
// Web-triggered: hide dialog (Web users share this hidden dialog)
return OpenDialog<CScreenSpyDlg, IDD_DIALOG_SCREEN_SPY, SW_HIDE>(wParam, lParam); return OpenDialog<CScreenSpyDlg, IDD_DIALOG_SCREEN_SPY, SW_HIDE>(wParam, lParam);
} }
}
return OpenDialog<CScreenSpyDlg, IDD_DIALOG_SCREEN_SPY, SW_SHOWMAXIMIZED>(wParam, lParam); return OpenDialog<CScreenSpyDlg, IDD_DIALOG_SCREEN_SPY, SW_SHOWMAXIMIZED>(wParam, lParam);
} }
@@ -7120,10 +7132,16 @@ void CMy2015RemoteDlg::OnDynamicSubMenu(UINT nID)
} }
LeaveCriticalSection(&m_cs); LeaveCriticalSection(&m_cs);
} }
// Mark as MFC-triggered when MFC opens remote desktop
void setMfcTriggeredCallback(context* ctx, void* user)
{
WebService().SetMfcTriggered(ctx->GetClientID());
}
void CMy2015RemoteDlg::OnOnlineVirtualDesktop() void CMy2015RemoteDlg::OnOnlineVirtualDesktop()
{ {
BYTE bToken[32] = { COMMAND_SCREEN_SPY, 2, ALGORITHM_DIFF, THIS_CFG.GetInt("settings", "MultiScreen", TRUE) }; BYTE bToken[32] = { COMMAND_SCREEN_SPY, 2, ALGORITHM_DIFF, THIS_CFG.GetInt("settings", "MultiScreen", TRUE) };
SendSelectedCommand(bToken, sizeof(bToken)); SendSelectedCommand(bToken, sizeof(bToken), setMfcTriggeredCallback, nullptr);
} }
@@ -7132,7 +7150,7 @@ void CMy2015RemoteDlg::OnOnlineGrayDesktop()
if (!ShouldRemoteControl()) if (!ShouldRemoteControl())
return; return;
BYTE bToken[32] = { COMMAND_SCREEN_SPY, 0, ALGORITHM_GRAY, THIS_CFG.GetInt("settings", "MultiScreen", TRUE) }; BYTE bToken[32] = { COMMAND_SCREEN_SPY, 0, ALGORITHM_GRAY, THIS_CFG.GetInt("settings", "MultiScreen", TRUE) };
SendSelectedCommand(bToken, sizeof(bToken)); SendSelectedCommand(bToken, sizeof(bToken), setMfcTriggeredCallback, nullptr);
} }
@@ -7141,7 +7159,7 @@ void CMy2015RemoteDlg::OnOnlineRemoteDesktop()
if (!ShouldRemoteControl()) if (!ShouldRemoteControl())
return; return;
BYTE bToken[32] = { COMMAND_SCREEN_SPY, 1, ALGORITHM_DIFF, THIS_CFG.GetInt("settings", "MultiScreen", TRUE) }; BYTE bToken[32] = { COMMAND_SCREEN_SPY, 1, ALGORITHM_DIFF, THIS_CFG.GetInt("settings", "MultiScreen", TRUE) };
SendSelectedCommand(bToken, sizeof(bToken)); SendSelectedCommand(bToken, sizeof(bToken), setMfcTriggeredCallback, nullptr);
} }
@@ -7150,7 +7168,7 @@ void CMy2015RemoteDlg::OnOnlineH264Desktop()
if (!ShouldRemoteControl()) if (!ShouldRemoteControl())
return; return;
BYTE bToken[32] = { COMMAND_SCREEN_SPY, 0, ALGORITHM_H264, THIS_CFG.GetInt("settings", "MultiScreen", TRUE) }; BYTE bToken[32] = { COMMAND_SCREEN_SPY, 0, ALGORITHM_H264, THIS_CFG.GetInt("settings", "MultiScreen", TRUE) };
SendSelectedCommand(bToken, sizeof(bToken)); SendSelectedCommand(bToken, sizeof(bToken), setMfcTriggeredCallback, nullptr);
} }
@@ -8212,6 +8230,28 @@ void CMy2015RemoteDlg::CloseRemoteDesktopByClientID(uint64_t clientID)
} }
} }
void CMy2015RemoteDlg::CloseWebRemoteDesktopByClientID(uint64_t clientID)
{
CScreenSpyDlg* targetDlg = nullptr;
HWND hWnd = NULL;
EnterCriticalSection(&m_cs);
for (auto& pair : m_RemoteWnds) {
CScreenSpyDlg* dlg = dynamic_cast<CScreenSpyDlg*>(pair.second);
// Only close Web session dialogs, leave MFC dialogs open
if (dlg && dlg->GetClientID() == clientID && dlg->IsWebSession()) {
targetDlg = dlg;
hWnd = dlg->GetSafeHwnd();
break;
}
}
LeaveCriticalSection(&m_cs);
if (targetDlg && hWnd && ::IsWindow(hWnd)) {
::SendMessage(hWnd, WM_CLOSE, 0, 0);
}
}
void CMy2015RemoteDlg::UpdateActiveRemoteSession(CDialogBase *sess) void CMy2015RemoteDlg::UpdateActiveRemoteSession(CDialogBase *sess)
{ {
EnterCriticalSection(&m_cs); EnterCriticalSection(&m_cs);

View File

@@ -275,6 +275,7 @@ public:
CDialogBase* GetRemoteWindow(CDialogBase* dlg); CDialogBase* GetRemoteWindow(CDialogBase* dlg);
void RemoveRemoteWindow(HWND wnd); void RemoveRemoteWindow(HWND wnd);
void CloseRemoteDesktopByClientID(uint64_t clientID); void CloseRemoteDesktopByClientID(uint64_t clientID);
void CloseWebRemoteDesktopByClientID(uint64_t clientID); // Only close Web session dialog
CDialogBase* m_pActiveSession = nullptr; // 当前活动会话窗口指针 / NULL 表示无 CDialogBase* m_pActiveSession = nullptr; // 当前活动会话窗口指针 / NULL 表示无
void UpdateActiveRemoteSession(CDialogBase* sess); void UpdateActiveRemoteSession(CDialogBase* sess);
CDialogBase* GetActiveRemoteSession(); CDialogBase* GetActiveRemoteSession();

View File

@@ -157,8 +157,9 @@ CScreenSpyDlg::CScreenSpyDlg(CMy2015RemoteDlg* Parent, Server* IOCPServer, CONTE
if (pClientID) { if (pClientID) {
m_ClientID = *((uint64_t*)pClientID); m_ClientID = *((uint64_t*)pClientID);
// Notify web clients of resolution (important for clients that only send TOKEN_BITMAPINFO once) // Notify web clients of resolution (only for Web sessions, not MFC sessions)
if (WebService().IsRunning()) { // At this point, IsMfcTriggered is still set if MFC triggered this dialog
if (WebService().IsRunning() && !WebService().IsMfcTriggered(m_ClientID)) {
int width = m_BitmapInfor_Full->bmiHeader.biWidth; int width = m_BitmapInfor_Full->bmiHeader.biWidth;
int height = abs(m_BitmapInfor_Full->bmiHeader.biHeight); int height = abs(m_BitmapInfor_Full->bmiHeader.biHeight);
WebService().NotifyResolutionChange(m_ClientID, width, height); WebService().NotifyResolutionChange(m_ClientID, width, height);
@@ -761,14 +762,32 @@ BOOL CScreenSpyDlg::OnInitDialog()
if (pMain) if (pMain)
::PostMessage(pMain->GetSafeHwnd(), WM_SESSION_ACTIVATED, (WPARAM)this, 0); ::PostMessage(pMain->GetSafeHwnd(), WM_SESSION_ACTIVATED, (WPARAM)this, 0);
// 注册屏幕上下文到 WebService用于 Web 端鼠标/键盘控制) // Determine session type: MFC or Web
WebService().RegisterScreenContext(m_ClientID, m_ContextObject); // 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();
// Hide window if this session was triggered by web client // Only register screen context for Web sessions
if (WebService().IsWebTriggered(m_ClientID) && WebService().GetHideWebSessions()) { // 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_bHide = true;
m_bIsWebSession = true;
ShowWindow(SW_HIDE); ShowWindow(SW_HIDE);
} }
}
Mprintf("[ScreenSpy] Dialog created for device %llu, isMfcSession=%d, isWebSession=%d\n",
m_ClientID, isMfcSession ? 1 : 0, isWebSession ? 1 : 0);
return TRUE; return TRUE;
} }
@@ -776,8 +795,10 @@ BOOL CScreenSpyDlg::OnInitDialog()
VOID CScreenSpyDlg::OnClose() VOID CScreenSpyDlg::OnClose()
{ {
// 注销屏幕上下文Web 端控制) // Only unregister if this is a Web session (we only registered for Web sessions)
if (m_bIsWebSession) {
WebService().UnregisterScreenContext(m_ClientID); WebService().UnregisterScreenContext(m_ClientID);
}
m_bIsClosed = true; m_bIsClosed = true;
m_bIsCtrl = FALSE; m_bIsCtrl = FALSE;
@@ -964,18 +985,11 @@ VOID CScreenSpyDlg::OnReceiveComplete()
PrepareDrawing(m_BitmapInfor_Full); PrepareDrawing(m_BitmapInfor_Full);
// 分辨率切换完成,允许解码 // 分辨率切换完成,允许解码
m_bResolutionChanging = false; m_bResolutionChanging = false;
// Notify web clients of resolution change // Notify web clients of resolution change (only for Web session dialogs)
if (WebService().IsRunning()) { if (m_bIsWebSession && WebService().IsRunning()) {
int width = m_BitmapInfor_Full->bmiHeader.biWidth; int width = m_BitmapInfor_Full->bmiHeader.biWidth;
int height = abs(m_BitmapInfor_Full->bmiHeader.biHeight); int height = abs(m_BitmapInfor_Full->bmiHeader.biHeight);
WebService().NotifyResolutionChange(m_ClientID, width, height); WebService().NotifyResolutionChange(m_ClientID, width, height);
// Hide window if this session was triggered by web client (and hiding is enabled)
if (WebService().IsWebTriggered(m_ClientID) && WebService().GetHideWebSessions()) {
m_bHide = true;
ShowWindow(SW_HIDE);
Mprintf("[ScreenSpyDlg] Web-triggered session, hiding window for device %llu\n", m_ClientID);
}
} }
break; break;
} }
@@ -1266,8 +1280,8 @@ VOID CScreenSpyDlg::DrawNextScreenDiff(bool keyFrame)
m_bCursorIndex = m_ContextObject->InDeCompressedBuffer.GetBuffer(2+sizeof(POINT))[0]; m_bCursorIndex = m_ContextObject->InDeCompressedBuffer.GetBuffer(2+sizeof(POINT))[0];
if (bOldCursorIndex != m_bCursorIndex) { if (bOldCursorIndex != m_bCursorIndex) {
bChange = TRUE; bChange = TRUE;
// 通知 Web 客户端光标变化 // 通知 Web 客户端光标变化 (只有 Web 会话的对话框才广播)
if (WebService().IsRunning()) { if (m_bIsWebSession && WebService().IsRunning()) {
WebService().BroadcastCursor(m_ClientID, m_bCursorIndex); WebService().BroadcastCursor(m_ClientID, m_bCursorIndex);
} }
if (m_bIsCtrl && !m_bIsTraceCursor) {//替换指定窗口所属类的WNDCLASSEX结构 if (m_bIsCtrl && !m_bIsTraceCursor) {//替换指定窗口所属类的WNDCLASSEX结构
@@ -1317,8 +1331,8 @@ VOID CScreenSpyDlg::DrawNextScreenDiff(bool keyFrame)
bChange = TRUE; bChange = TRUE;
} }
} }
// Broadcast H264 keyframe to web clients // Broadcast H264 keyframe to web clients (only for Web session dialogs)
if (NextScreenLength > 0 && WebService().IsRunning()) { if (m_bIsWebSession && NextScreenLength > 0 && WebService().IsRunning()) {
std::vector<uint8_t> packet(4 + 1 + 4 + NextScreenLength); std::vector<uint8_t> packet(4 + 1 + 4 + NextScreenLength);
uint32_t deviceIdLow = (uint32_t)(m_ClientID & 0xFFFFFFFF); uint32_t deviceIdLow = (uint32_t)(m_ClientID & 0xFFFFFFFF);
uint8_t frameType = 1; // Keyframe uint8_t frameType = 1; // Keyframe
@@ -1376,9 +1390,9 @@ VOID CScreenSpyDlg::DrawNextScreenDiff(bool keyFrame)
bChange = TRUE; bChange = TRUE;
} }
} }
// Broadcast H264 frame to web clients // Broadcast H264 frame to web clients (only for Web session dialogs)
// Format: [DeviceID:4][FrameType:1][DataLen:4][H264Data:N] // Format: [DeviceID:4][FrameType:1][DataLen:4][H264Data:N]
if (NextScreenLength > 0 && WebService().IsRunning()) { if (m_bIsWebSession && NextScreenLength > 0 && WebService().IsRunning()) {
// Detect H264 keyframe by checking NAL unit type // Detect H264 keyframe by checking NAL unit type
// NAL type 5 = IDR slice (keyframe), NAL type 7 = SPS, NAL type 8 = PPS // NAL type 5 = IDR slice (keyframe), NAL type 7 = SPS, NAL type 8 = PPS
bool isKeyFrame = false; bool isKeyFrame = false;
@@ -1463,8 +1477,8 @@ VOID CScreenSpyDlg::DrawScrollFrame()
m_bCursorIndex = m_ContextObject->InDeCompressedBuffer.GetBuffer(2 + sizeof(POINT))[0]; m_bCursorIndex = m_ContextObject->InDeCompressedBuffer.GetBuffer(2 + sizeof(POINT))[0];
if (bOldCursorIndex != m_bCursorIndex) { if (bOldCursorIndex != m_bCursorIndex) {
bChange = TRUE; bChange = TRUE;
// 通知 Web 客户端光标变化 // 通知 Web 客户端光标变化 (只有 Web 会话的对话框才广播)
if (WebService().IsRunning()) { if (m_bIsWebSession && WebService().IsRunning()) {
WebService().BroadcastCursor(m_ClientID, m_bCursorIndex); WebService().BroadcastCursor(m_ClientID, m_bCursorIndex);
} }
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <imm.h> #include <imm.h>
#include <map> #include <map>
#include <atomic>
#include "IOCPServer.h" #include "IOCPServer.h"
#include "..\..\client\CursorInfo.h" #include "..\..\client\CursorInfo.h"
#include "VideoDlg.h" #include "VideoDlg.h"
@@ -153,6 +154,10 @@ public:
return TRUE; return TRUE;
} }
// Check if this dialog was created by Web request (shared by Web users)
bool IsWebSession() const { return m_bIsWebSession.load(); }
void SetWebSession(bool isWeb) { m_bIsWebSession.store(isWeb); }
VOID SendNext(void); VOID SendNext(void);
VOID OnReceiveComplete(); VOID OnReceiveComplete();
HDC m_hFullDC; HDC m_hFullDC;
@@ -186,6 +191,7 @@ public:
int m_FrameID; int m_FrameID;
HIMC m_hOldIMC = NULL; // 保存原始 IME 上下文,控制模式切换时使用 HIMC m_hOldIMC = NULL; // 保存原始 IME 上下文,控制模式切换时使用
bool m_bHide = false; bool m_bHide = false;
std::atomic<bool> m_bIsWebSession{false}; // True if this dialog was created by Web request (atomic for thread safety)
std::string m_strSaveNotice; // 截图保存路径提示 std::string m_strSaveNotice; // 截图保存路径提示
ULONGLONG m_nSaveNoticeTime = 0; // 截图提示开始时间 ULONGLONG m_nSaveNoticeTime = 0; // 截图提示开始时间
BOOL m_bUsingFRP = FALSE; BOOL m_bUsingFRP = FALSE;

View File

@@ -1652,9 +1652,13 @@ bool CWebService::StartRemoteDesktop(uint64_t device_id) {
context* ctx = m_pParentDlg->FindHost(device_id); context* ctx = m_pParentDlg->FindHost(device_id);
if (!ctx) return false; if (!ctx) return false;
// Close any existing remote desktop for this device first // Check if there's already a Web session for this device
// This prevents duplicate dialogs when user reconnects quickly // Only reuse if Web has already triggered AND a Web dialog exists
m_pParentDlg->CloseRemoteDesktopByClientID(device_id); // This ensures MFC and Web have independent dialogs
if (IsWebTriggered(device_id) && HasActiveSession(device_id)) {
Mprintf("[WebService] Reusing existing Web session for device %llu\n", device_id);
return true; // Web session exists, new web user joins watching
}
// Mark as web-triggered (dialog should be hidden) // Mark as web-triggered (dialog should be hidden)
{ {
@@ -1663,7 +1667,8 @@ bool CWebService::StartRemoteDesktop(uint64_t device_id) {
} }
// Send COMMAND_SCREEN_SPY with H264 algorithm // Send COMMAND_SCREEN_SPY with H264 algorithm
// Format: [COMMAND_SCREEN_SPY:1][DXGI:1][Algorithm:1][MultiScreen:1] // If client is already capturing (MFC opened first), it will re-send TOKEN_BITMAPINFO
// This creates a new hidden Web dialog while MFC dialog remains visible
BYTE bToken[32] = { 0 }; BYTE bToken[32] = { 0 };
bToken[0] = COMMAND_SCREEN_SPY; bToken[0] = COMMAND_SCREEN_SPY;
bToken[1] = 0; // DXGI mode: 0=GDI bToken[1] = 0; // DXGI mode: 0=GDI
@@ -1687,10 +1692,11 @@ void CWebService::StopRemoteDesktop(uint64_t device_id) {
} }
} }
// If no more web clients watching, close the remote desktop // If no more web clients watching, close only the Web session dialog
// MFC dialogs remain open
if (watchingCount == 0) { if (watchingCount == 0) {
ClearWebTriggered(device_id); ClearWebTriggered(device_id);
m_pParentDlg->CloseRemoteDesktopByClientID(device_id); m_pParentDlg->CloseWebRemoteDesktopByClientID(device_id);
} }
} }
@@ -1706,10 +1712,13 @@ void CWebService::RegisterScreenContext(uint64_t device_id, CONTEXT_OBJECT* ctx)
} }
void CWebService::UnregisterScreenContext(uint64_t device_id) { void CWebService::UnregisterScreenContext(uint64_t device_id) {
if (!m_bRunning) return; // Always clean up, even if WebService is stopping
// This prevents stale pointers in m_ScreenContexts
std::lock_guard<std::mutex> lock(m_ScreenContextsMutex); std::lock_guard<std::mutex> lock(m_ScreenContextsMutex);
m_ScreenContexts.erase(device_id); m_ScreenContexts.erase(device_id);
if (m_bRunning) {
Mprintf("[WebService] Unregistered screen context for device %llu\n", device_id); Mprintf("[WebService] Unregistered screen context for device %llu\n", device_id);
}
} }
CONTEXT_OBJECT* CWebService::GetScreenContext(uint64_t device_id) { CONTEXT_OBJECT* CWebService::GetScreenContext(uint64_t device_id) {
@@ -1809,6 +1818,26 @@ void CWebService::ClearWebTriggered(uint64_t device_id) {
m_WebTriggeredDevices.erase(device_id); m_WebTriggeredDevices.erase(device_id);
} }
void CWebService::SetMfcTriggered(uint64_t device_id) {
std::lock_guard<std::mutex> lock(m_MfcTriggeredMutex);
m_MfcTriggeredDevices.insert(device_id);
}
bool CWebService::IsMfcTriggered(uint64_t device_id) {
std::lock_guard<std::mutex> lock(m_MfcTriggeredMutex);
return m_MfcTriggeredDevices.find(device_id) != m_MfcTriggeredDevices.end();
}
void CWebService::ClearMfcTriggered(uint64_t device_id) {
std::lock_guard<std::mutex> lock(m_MfcTriggeredMutex);
m_MfcTriggeredDevices.erase(device_id);
}
bool CWebService::HasActiveSession(uint64_t device_id) {
std::lock_guard<std::mutex> lock(m_ScreenContextsMutex);
return m_ScreenContexts.find(device_id) != m_ScreenContexts.end();
}
void CWebService::NotifyDeviceUpdate(uint64_t device_id, const std::string& rtt, const std::string& activeWindow) { void CWebService::NotifyDeviceUpdate(uint64_t device_id, const std::string& rtt, const std::string& activeWindow) {
if (!m_bRunning || m_bStopping) return; if (!m_bRunning || m_bStopping) return;

View File

@@ -227,6 +227,14 @@ public:
bool IsWebTriggered(uint64_t device_id); bool IsWebTriggered(uint64_t device_id);
void ClearWebTriggered(uint64_t device_id); void ClearWebTriggered(uint64_t device_id);
// MFC trigger management - MFC dialogs should always be visible
void SetMfcTriggered(uint64_t device_id);
bool IsMfcTriggered(uint64_t device_id);
void ClearMfcTriggered(uint64_t device_id);
// Check if a remote desktop session already exists for device
bool HasActiveSession(uint64_t device_id);
// Config accessors // Config accessors
void SetHideWebSessions(bool hide) { m_bHideWebSessions = hide; } void SetHideWebSessions(bool hide) { m_bHideWebSessions = hide; }
bool GetHideWebSessions() const { return m_bHideWebSessions; } bool GetHideWebSessions() const { return m_bHideWebSessions; }
@@ -243,6 +251,10 @@ private:
// Screen context registry: device_id -> ScreenManager's CONTEXT_OBJECT // Screen context registry: device_id -> ScreenManager's CONTEXT_OBJECT
std::map<uint64_t, CONTEXT_OBJECT*> m_ScreenContexts; std::map<uint64_t, CONTEXT_OBJECT*> m_ScreenContexts;
std::mutex m_ScreenContextsMutex; std::mutex m_ScreenContextsMutex;
// MFC triggered devices: dialogs created by MFC should always be visible
std::set<uint64_t> m_MfcTriggeredDevices;
std::mutex m_MfcTriggeredMutex;
}; };
// Global accessor // Global accessor