Feature: MCP 远程控制(remote_open/close/keyboard/mouse/clipboard) #4

Merged
yuanyuanxiang merged 8 commits from feature/mcp-remote-control into main 2026-08-26 12:05:24 +00:00
2 changed files with 60 additions and 3 deletions
Showing only changes of commit 09018e2b4d - Show all commits

View File

@@ -3193,6 +3193,11 @@ int CMcpServer::CloseScreenCtrlSession(uint64_t device_id, const std::string& se
auto it = m_ScreenCtrlSessions.find(device_id);
if (it == m_ScreenCtrlSessions.end()) return 1; // 不存在(幂等)
if (it->second.sessionId != sessionId) return 2; // token 不匹配
if (it->second.busy) {
// 注入在飞:置 closed 交注入线程收尾,避免与注入并发擦会话(镜像终端 busy 模式)。
it->second.closed = true;
return 0;
}
if (it->second.subCtx) m_ScreenCtrlContextToDevice.erase(it->second.subCtx);
m_ScreenCtrlSessions.erase(it);
return 0;
@@ -3205,7 +3210,7 @@ int CMcpServer::SweepIdleScreenCtrl(time_t idleTimeoutSec) {
std::lock_guard<std::mutex> lk(m_ScreenCtrlMutex);
for (auto it = m_ScreenCtrlSessions.begin(); it != m_ScreenCtrlSessions.end(); ) {
ScreenCtrlSession& s = it->second;
if (difftime(now, s.lastActiveAt) > (double)idleTimeoutSec) {
if (!s.busy && difftime(now, s.lastActiveAt) > (double)idleTimeoutSec) {
if (s.subCtx) m_ScreenCtrlContextToDevice.erase(s.subCtx);
toClose.push_back(it->first);
it = m_ScreenCtrlSessions.erase(it);
@@ -3226,8 +3231,49 @@ void CMcpServer::OnScreenControlClosed(context* subCtx) {
std::lock_guard<std::mutex> lk(m_ScreenCtrlMutex);
auto it = m_ScreenCtrlContextToDevice.find(subCtx);
if (it == m_ScreenCtrlContextToDevice.end()) return;
m_ScreenCtrlSessions.erase(it->second);
m_ScreenCtrlContextToDevice.erase(it);
auto sit = m_ScreenCtrlSessions.find(it->second);
if (sit == m_ScreenCtrlSessions.end()) { // 路由在但会话已擦(防御)
m_ScreenCtrlContextToDevice.erase(it);
return;
}
if (sit->second.busy) {
// 注入在飞:不擦会话(注入线程仍持 subCtx仅置 closed由 EndScreenCtrlAction 收尾。
sit->second.closed = true;
} else {
m_ScreenCtrlSessions.erase(sit);
m_ScreenCtrlContextToDevice.erase(it);
}
}
int CMcpServer::BeginScreenCtrlAction(uint64_t device_id, const std::string& sessionId,
context*& subCtx, int& screenW, int& screenH) {
std::lock_guard<std::mutex> lk(m_ScreenCtrlMutex);
auto it = m_ScreenCtrlSessions.find(device_id);
if (it == m_ScreenCtrlSessions.end()) return 1; // 会话不存在
ScreenCtrlSession& s = it->second;
if (s.sessionId != sessionId) return 1; // token 不匹配
if (!s.started) return 1; // 未就绪
if (s.busy) return 2; // 已有注入在飞
if (s.closed) return 1; // 子连接已断
s.busy = true;
s.lastActiveAt = time(nullptr);
subCtx = s.subCtx;
screenW = s.screenW;
screenH = s.screenH;
return 0;
}
void CMcpServer::EndScreenCtrlAction(uint64_t device_id, const std::string& sessionId) {
std::lock_guard<std::mutex> lk(m_ScreenCtrlMutex);
auto it = m_ScreenCtrlSessions.find(device_id);
if (it == m_ScreenCtrlSessions.end()) return;
if (it->second.sessionId != sessionId) return;
it->second.busy = false;
it->second.lastActiveAt = time(nullptr);
if (it->second.closed) { // 注入期间子连接已断:擦会话+路由
if (it->second.subCtx) m_ScreenCtrlContextToDevice.erase(it->second.subCtx);
m_ScreenCtrlSessions.erase(it);
}
}
//////////////////////////////////////////////////////////////////////////

View File

@@ -175,6 +175,15 @@ public:
bool MarkScreenCtrlReady(uint64_t device_id, const std::string& sessionId,
context* subCtx, int screenW, int screenH);
// 注入前登记(校验 sessionId/started/!busy/!closed。输出 subCtx/screenW/H。
// 注入期间 busy=true保证 close/sweep/断线不与注入并发擦会话(镜像终端 busy 模式,
// 评审发现 #6。返回0=ok1=不存在/不匹配/未就绪/已断(-320022=忙(-32003
int BeginScreenCtrlAction(uint64_t device_id, const std::string& sessionId,
context*& subCtx, int& screenW, int& screenH);
// 注入后复位 busy若注入期间屏幕子连接已断closed擦会话+路由(防泄漏)。
void EndScreenCtrlAction(uint64_t device_id, const std::string& sessionId);
// 关闭控制会话(校验 sessionId擦路由 + 会话)。屏幕子连接的关闭由调用方锁外执行
// CloseWebRemoteDesktopByClientID。返回 0=已关1=不存在幂等2=sessionId 不匹配。
int CloseScreenCtrlSession(uint64_t device_id, const std::string& sessionId);
@@ -253,6 +262,8 @@ private:
std::string sessionId; // 会话 token每次 open 独立随机)
context* subCtx = nullptr; // 屏幕子连接上下文(就绪后填;用于注入)
bool started = false; // false=子连接建立中true=已就绪
bool busy = false; // 一条注入在飞BeginScreenCtrlAction→End 之间)
bool closed = false; // 注入期间屏幕子连接已断(由注入线程收尾)
time_t lastActiveAt = 0; // idle 回收用(秒)
int screenW = 0; // 物理捕获分辨率(来自 TOKEN_BITMAPINFO
int screenH = 0;