From 5a92c3306f097901604539749d7cd98f59e471e9 Mon Sep 17 00:00:00 2001
From: yuanyuanxiang <962914132@qq.com>
Date: Thu, 14 May 2026 23:57:48 +0200
Subject: [PATCH] Feature: Web remote terminal (xterm.js + mobile UX polish)
---
server/2015Remote/2015Remote.rc | Bin 155382 -> 155820 bytes
server/2015Remote/2015RemoteDlg.cpp | 37 +-
server/2015Remote/2015Remote_vs2015.vcxproj | 5 +
.../2015Remote_vs2015.vcxproj.filters | 5 +
server/2015Remote/WebPage.h | 342 +++++++++++++++++-
server/2015Remote/WebService.cpp | 306 +++++++++++++++-
server/2015Remote/WebService.h | 36 ++
server/2015Remote/res/web/fit.min.js | 8 +
server/2015Remote/res/web/xterm.css | 209 +++++++++++
server/2015Remote/res/web/xterm.min.js | 8 +
server/2015Remote/resource.h | 5 +-
11 files changed, 953 insertions(+), 8 deletions(-)
create mode 100644 server/2015Remote/res/web/fit.min.js
create mode 100644 server/2015Remote/res/web/xterm.css
create mode 100644 server/2015Remote/res/web/xterm.min.js
diff --git a/server/2015Remote/2015Remote.rc b/server/2015Remote/2015Remote.rc
index 01705bdf7487c5aa36f77949a4954aa799c81c6e..c5e25989197cf1aeb3841ac314ca613122ea2dcc 100644
GIT binary patch
delta 254
zcmeyimvhZQ&W0AoElkf`rpLH2afpXAxH32~#4|)NgaBDV48B0N7eny$KszSq$p?hE
zJj)qU8Il-cfUtt01jsI8$Ysz2;!K7-Af3ff%%H@;%fQ9p$>0Li9?yWyKxc+vpplBU
wOx`$*O9mQ4j6rTdYeJwdG_+*02Dwlc;lebat4dH^H~qdN)9&rlT$vIi0X~B@HUIzs
delta 19
bcmZ2;kn`JK&W0AoElkf`w$E{65|9D_TY?AO
diff --git a/server/2015Remote/2015RemoteDlg.cpp b/server/2015Remote/2015RemoteDlg.cpp
index e6a35cd..c70cbca 100644
--- a/server/2015Remote/2015RemoteDlg.cpp
+++ b/server/2015Remote/2015RemoteDlg.cpp
@@ -4587,6 +4587,12 @@ BOOL CALLBACK CMy2015RemoteDlg::OfflineProc(CONTEXT_OBJECT* ContextObject)
if (!g_2015RemoteDlg || g_2015RemoteDlg->isClosed)
return FALSE;
+ // Web 终端的 shell 子上下文断开:通知 WebService 清理 session(含通知前端)。
+ // 在 RemoveFromHostList 之前做,避免 ctx 被释放后 WebService 还持有悬空指针。
+ if (WebService().IsRunning() && WebService().IsTerminalContext(ContextObject)) {
+ WebService().OnTerminalClosed(ContextObject);
+ }
+
SOCKET nSocket = ContextObject->sClientSocket;
CDialogBase* p = (CDialogBase*)ContextObject->hDlg;
@@ -4918,6 +4924,19 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject)
unsigned cmd = ContextObject->InDeCompressedBuffer.GetBYTE(0);
LPBYTE szBuffer = ContextObject->InDeCompressedBuffer.GetBuffer();
unsigned len = ContextObject->InDeCompressedBuffer.GetBufferLen();
+
+ // ===== Web 终端的 shell 子上下文:被 WebService 接管时,所有数据走 OnTerminalData =====
+ // 这里覆盖一个特殊路径:Web 接管的 shell 子上下文不开 MFC 对话框,hDlg 一直为 NULL,
+ // 因此每个数据包都会走到这个 MessageHandle。我们把字节直接转发给 WebService。
+ if (WebService().IsRunning() && WebService().IsTerminalContext(ContextObject)) {
+ if (len == 1 && cmd == TOKEN_TERMINAL_CLOSE) {
+ WebService().OnTerminalClosed(ContextObject);
+ } else {
+ WebService().OnTerminalData(ContextObject, szBuffer, len);
+ }
+ return;
+ }
+
// 【L】:主机上下线和授权
// 【x】:对话框相关功能
switch (cmd) {
@@ -5676,11 +5695,25 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject)
g_2015RemoteDlg->SendMessage(WM_OPENTALKDIALOG, 0, (LPARAM)ContextObject);
break;
}
- case TOKEN_SHELL_START: { // Windows 远程终端
+ case TOKEN_SHELL_START: { // Windows 老 cmd 管道终端
+ // 如果是 Web 触发的 term_open,把 shell 子上下文交给 WebService 而不是开 MFC dialog
+ uint64_t devId = ContextObject->GetClientID();
+ if (WebService().IsRunning() && WebService().IsTermPending(devId)) {
+ WebService().RegisterTerminalContext(devId, ContextObject, /*isPty*/false);
+ // hDlg 留 NULL:后续数据继续走 MessageHandle 顶部的 IsTerminalContext 分支
+ break;
+ }
g_2015RemoteDlg->SendMessage(WM_OPENSHELLDIALOG, 0, (LPARAM)ContextObject);
break;
}
- case TOKEN_TERMINAL_START: { // Linux PTY 终端 (WebView2 + xterm.js)
+ case TOKEN_TERMINAL_START: { // 现代 PTY 终端 (Linux/macOS/Windows ConPTY)
+ // 同上:Web 触发优先级最高,直接 WebService 接管
+ uint64_t devId = ContextObject->GetClientID();
+ if (WebService().IsRunning() && WebService().IsTermPending(devId)) {
+ WebService().RegisterTerminalContext(devId, ContextObject, /*isPty*/true);
+ break;
+ }
+
// 三个前置条件,缺任何一个都回退到经典终端,并把原因贴到信息列表。
// SYSTEM 场景:WebView2 不支持 LocalSystem token,会出现"窗口能弹但页面空白",
// 显式拦截一次,避免用户误以为是 bug。
diff --git a/server/2015Remote/2015Remote_vs2015.vcxproj b/server/2015Remote/2015Remote_vs2015.vcxproj
index 45c6567..0749146 100644
--- a/server/2015Remote/2015Remote_vs2015.vcxproj
+++ b/server/2015Remote/2015Remote_vs2015.vcxproj
@@ -601,6 +601,11 @@
+
+
+
+
+
diff --git a/server/2015Remote/2015Remote_vs2015.vcxproj.filters b/server/2015Remote/2015Remote_vs2015.vcxproj.filters
index 20c26e6..15ac41e 100644
--- a/server/2015Remote/2015Remote_vs2015.vcxproj.filters
+++ b/server/2015Remote/2015Remote_vs2015.vcxproj.filters
@@ -340,4 +340,9 @@
{17217547-dc35-4a87-859c-e8559529a909}
+
+
+
+
+
\ No newline at end of file
diff --git a/server/2015Remote/WebPage.h b/server/2015Remote/WebPage.h
index 25e0da3..44e151a 100644
--- a/server/2015Remote/WebPage.h
+++ b/server/2015Remote/WebPage.h
@@ -22,6 +22,8 @@ inline std::string GetWebPageHTML() {
+
+