3 Commits

Author SHA1 Message Date
yuanyuanxiang
3c74a8f7d3 Feat: Z button ROI region, sends COMMAND_SCREEN_ROI to restrict capture area 2026-06-14 07:03:11 +02:00
yuanyuanxiang
63ef75b7ce Feat: ROI screen capture with remote control support via COMMAND_SCREEN_ROI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 00:24:46 +02:00
yuanyuanxiang
1335d636da Feat: copy selected online host info to clipboard as CSV with menu bitmap 2026-06-12 19:13:23 +02:00
20 changed files with 290 additions and 50 deletions

View File

@@ -619,12 +619,12 @@ VOID IOCPClient::OnServerReceiving(CBuffer* m_CompressedBuffer, char* szBuffer,
FlagType flagType = CheckHead(szPacketFlag, encType); FlagType flagType = CheckHead(szPacketFlag, encType);
if (flagType == FLAG_UNKNOWN) { if (flagType == FLAG_UNKNOWN) {
// 打印诊断信息 // 打印诊断信息
std::string buf;
ULONG bufLen = m_CompressedBuffer->GetBufferLength(); ULONG bufLen = m_CompressedBuffer->GetBufferLength();
Mprintf("[ERROR] Unknown header! bufLen=%lu, first 16 bytes: ", bufLen);
for (int i = 0; i < 16 && i < (int)bufLen; ++i) { for (int i = 0; i < 16 && i < (int)bufLen; ++i) {
Mprintf("%02X ", (unsigned char)src[i]); char tmp[12]; sprintf(tmp, "%02X ", (unsigned char)src[i]); buf += tmp;
} }
Mprintf("\n"); Mprintf("[ERROR] Unknown header! bufLen=%lu, first 16 bytes: %s\n", bufLen, buf.c_str());
m_CompressedBuffer->ClearBuffer(); m_CompressedBuffer->ClearBuffer();
break; break;
} }

View File

@@ -168,6 +168,11 @@ public:
int m_nInstructionSet = 0; int m_nInstructionSet = 0;
int m_nBitRate = 0; // H264 编码码率 (kbps), 0=自动 int m_nBitRate = 0; // H264 编码码率 (kbps), 0=自动
// 感兴趣区域 (ROI)
RECT m_ROI = {0,0,0,0};
int m_nScaleSendWidth = 0;
int m_nScaleSendHeight = 0;
// 自定义光标相关 // 自定义光标相关
DWORD m_dwLastCursorHash = 0; // 上次发送的光标哈希 DWORD m_dwLastCursorHash = 0; // 上次发送的光标哈希
DWORD m_dwLastCursorSendTime = 0; // 上次发送光标的时间 DWORD m_dwLastCursorSendTime = 0; // 上次发送光标的时间
@@ -192,7 +197,8 @@ protected:
int m_nVScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN); int m_nVScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
public: public:
ScreenCapture(int n = 32, BYTE algo = ALGORITHM_DIFF, BOOL all = FALSE, int level = LEVEL_H264_SOFT) : ScreenCapture(int n = 32, BYTE algo = ALGORITHM_DIFF, BOOL all = FALSE, int level = LEVEL_H264_SOFT,
RECT rc = {0}, BOOL switchScreen = TRUE) :
m_ThreadPool(nullptr), m_FirstBuffer(nullptr), m_RectBuffer(nullptr), m_ThreadPool(nullptr), m_FirstBuffer(nullptr), m_RectBuffer(nullptr),
m_BitmapInfor_Full(nullptr), m_bAlgorithm(algo), m_SendQuality(100), m_BitmapInfor_Full(nullptr), m_bAlgorithm(algo), m_SendQuality(100),
m_ulFullWidth(0), m_ulFullHeight(0), m_bZoomed(false), m_wZoom(1), m_hZoom(1), m_ulFullWidth(0), m_ulFullHeight(0), m_bZoomed(false), m_wZoom(1), m_hZoom(1),
@@ -202,6 +208,7 @@ public:
m_bLastFrameWasScroll(false), m_nScrollDetectInterval(1), m_EncodeLevel(level) m_bLastFrameWasScroll(false), m_nScrollDetectInterval(1), m_EncodeLevel(level)
{ {
SetAlgorithm(algo); SetAlgorithm(algo);
m_ROI = rc;
m_BitmapInfor_Send = nullptr; m_BitmapInfor_Send = nullptr;
m_BmpZoomBuffer = nullptr; m_BmpZoomBuffer = nullptr;
m_BmpZoomFirst = nullptr; m_BmpZoomFirst = nullptr;
@@ -212,7 +219,7 @@ public:
m_nScreenCount = monitors.size(); m_nScreenCount = monitors.size();
m_bEnableMultiScreen = all; m_bEnableMultiScreen = all;
if (all && !monitors.empty()) { if (all && !monitors.empty()) {
int idx = index++ % (monitors.size()+1); int idx = (switchScreen ? index++ : index) % (monitors.size()+1);
if (idx == 0) { if (idx == 0) {
m_iScreenX = GetSystemMetrics(SM_XVIRTUALSCREEN); m_iScreenX = GetSystemMetrics(SM_XVIRTUALSCREEN);
m_iScreenY = GetSystemMetrics(SM_YVIRTUALSCREEN); m_iScreenY = GetSystemMetrics(SM_YVIRTUALSCREEN);
@@ -899,7 +906,8 @@ public:
bool shouldDetectScroll = !keyFrame && algo != ALGORITHM_H264 && bool shouldDetectScroll = !keyFrame && algo != ALGORITHM_H264 &&
m_bEnableScrollDetect && m_bServerSupportsScroll && m_pScrollDetector && m_bEnableScrollDetect && m_bServerSupportsScroll && m_pScrollDetector &&
!m_bLastFrameWasScroll && m_nScrollDetectInterval > 0 && !m_bLastFrameWasScroll && m_nScrollDetectInterval > 0 &&
(m_FrameID % m_nScrollDetectInterval == 0); (m_FrameID % m_nScrollDetectInterval == 0) &&
!m_nScaleSendWidth;
if (shouldDetectScroll) { if (shouldDetectScroll) {
int scrollAmount = m_pScrollDetector->DetectVerticalScroll(GetFirstBuffer(), nextData); int scrollAmount = m_pScrollDetector->DetectVerticalScroll(GetFirstBuffer(), nextData);
@@ -1045,9 +1053,14 @@ public:
// 鼠标位置转换:将服务端坐标(基于发送分辨率)转换为客户端坐标(原始分辨率) // 鼠标位置转换:将服务端坐标(基于发送分辨率)转换为客户端坐标(原始分辨率)
virtual void PointConversion(POINT& pt) const virtual void PointConversion(POINT& pt) const
{ {
// 0. ROI 偏移ROI 坐标系 → scale 坐标系
if (m_nScaleSendWidth) {
pt.x += m_ROI.left;
pt.y += m_ROI.top;
}
// 1. 处理图像缩小传输的坐标缩放maxWidth 限制) // 1. 处理图像缩小传输的坐标缩放maxWidth 限制)
int sendWidth = m_BitmapInfor_Send->bmiHeader.biWidth; int sendWidth = m_nScaleSendWidth ? m_nScaleSendWidth : (int)m_BitmapInfor_Send->bmiHeader.biWidth;
int sendHeight = m_BitmapInfor_Send->bmiHeader.biHeight; int sendHeight = m_nScaleSendHeight ? m_nScaleSendHeight : (int)m_BitmapInfor_Send->bmiHeader.biHeight;
if (sendWidth != m_ulFullWidth || sendHeight != m_ulFullHeight) { if (sendWidth != m_ulFullWidth || sendHeight != m_ulFullHeight) {
pt.x = (LONG)(pt.x * (double)m_ulFullWidth / sendWidth + 0.5); pt.x = (LONG)(pt.x * (double)m_ulFullWidth / sendWidth + 0.5);
pt.y = (LONG)(pt.y * (double)m_ulFullHeight / sendHeight + 0.5); pt.y = (LONG)(pt.y * (double)m_ulFullHeight / sendHeight + 0.5);
@@ -1074,12 +1087,17 @@ public:
pt.y = (LONG)(pt.y / m_hZoom); pt.y = (LONG)(pt.y / m_hZoom);
} }
// 1'. full → send 缩放(位图下采样传输时) // 1'. full → send 缩放(位图下采样传输时)
int sendWidth = m_BitmapInfor_Send->bmiHeader.biWidth; int sendWidth = m_nScaleSendWidth ? m_nScaleSendWidth : (int)m_BitmapInfor_Send->bmiHeader.biWidth;
int sendHeight = m_BitmapInfor_Send->bmiHeader.biHeight; int sendHeight = m_nScaleSendHeight ? m_nScaleSendHeight : (int)m_BitmapInfor_Send->bmiHeader.biHeight;
if (sendWidth != (int)m_ulFullWidth || sendHeight != (int)m_ulFullHeight) { if (sendWidth != (int)m_ulFullWidth || sendHeight != (int)m_ulFullHeight) {
pt.x = (LONG)((double)pt.x * sendWidth / m_ulFullWidth + 0.5); pt.x = (LONG)((double)pt.x * sendWidth / m_ulFullWidth + 0.5);
pt.y = (LONG)((double)pt.y * sendHeight / m_ulFullHeight + 0.5); pt.y = (LONG)((double)pt.y * sendHeight / m_ulFullHeight + 0.5);
} }
// 0'. scale 坐标系 → ROI 坐标系
if (m_nScaleSendWidth) {
pt.x -= m_ROI.left;
pt.y -= m_ROI.top;
}
} }
// 获取位图结构信息 // 获取位图结构信息
@@ -1164,11 +1182,25 @@ public: // 纯虚接口
// 获取下一帧屏幕 // 获取下一帧屏幕
virtual LPBYTE ScanNextScreen() = 0; virtual LPBYTE ScanNextScreen() = 0;
// ROI crop从 srcscale 后缓冲)裁剪到 targetsrc==target 时 in-place 安全
LPBYTE applyROICrop(LPBYTE target, LPBYTE src, int scaledW, int scaledH)
{
int rw = m_BitmapInfor_Send->bmiHeader.biWidth, rh = m_BitmapInfor_Send->bmiHeader.biHeight;
for (int row = 0; row < rh; row++)
memmove(target + row * rw * 4, src + ((scaledH - m_ROI.bottom + row) * scaledW + m_ROI.left) * 4, rw * 4);
return target;
}
virtual LPBYTE scaleBitmap(LPBYTE target, LPBYTE bitmap) virtual LPBYTE scaleBitmap(LPBYTE target, LPBYTE bitmap)
{ {
if (m_ulFullWidth == m_BitmapInfor_Send->bmiHeader.biWidth && m_ulFullHeight == m_BitmapInfor_Send->bmiHeader.biHeight) int scaledW = m_nScaleSendWidth ? m_nScaleSendWidth : (int)m_BitmapInfor_Send->bmiHeader.biWidth;
return bitmap; int scaledH = m_nScaleSendHeight ? m_nScaleSendHeight : (int)m_BitmapInfor_Send->bmiHeader.biHeight;
return ScaleBitmap(target, (uint8_t*)bitmap, m_ulFullWidth, m_ulFullHeight, m_BitmapInfor_Send->bmiHeader.biWidth, LPBYTE src = bitmap;
m_BitmapInfor_Send->bmiHeader.biHeight, m_nInstructionSet); if ((ULONG)scaledW != m_ulFullWidth || (ULONG)scaledH != m_ulFullHeight)
src = ScaleBitmap(target, (uint8_t*)bitmap, m_ulFullWidth, m_ulFullHeight, scaledW, scaledH, m_nInstructionSet);
if (m_nScaleSendWidth) {
src = applyROICrop(target, src, scaledW, scaledH);
}
return src;
} }
}; };

View File

@@ -25,11 +25,12 @@ private:
BYTE* m_NextBuffer = nullptr; BYTE* m_NextBuffer = nullptr;
public: public:
ScreenCapturerDXGI(BYTE algo, int gop = DEFAULT_GOP, BOOL all = FALSE, int level = LEVEL_H264_SOFT) ScreenCapturerDXGI(BYTE algo, int gop = DEFAULT_GOP, BOOL all = FALSE, int level = LEVEL_H264_SOFT,
: ScreenCapture(32, algo, all, level) RECT rc = {0}, BOOL switchScreen = TRUE)
: ScreenCapture(32, algo, all, level, rc, switchScreen)
{ {
m_GOP = gop; m_GOP = gop;
InitDXGI(all); InitDXGI(all, switchScreen);
Mprintf("Capture screen with DXGI: GOP= %d\n", m_GOP); Mprintf("Capture screen with DXGI: GOP= %d\n", m_GOP);
} }
@@ -47,7 +48,7 @@ public:
return TRUE; return TRUE;
} }
void InitDXGI(BOOL all) void InitDXGI(BOOL all, BOOL switchScreen)
{ {
m_iScreenX = 0; m_iScreenX = 0;
m_iScreenY = 0; m_iScreenY = 0;
@@ -72,7 +73,7 @@ public:
// 4. 获取 DXGI 输出(屏幕) // 4. 获取 DXGI 输出(屏幕)
static UINT screen = 0; static UINT screen = 0;
HRESULT r = dxgiAdapter->EnumOutputs(screen++, &dxgiOutput); HRESULT r = dxgiAdapter->EnumOutputs(switchScreen ? screen++ : screen, &dxgiOutput);
if (r == DXGI_ERROR_NOT_FOUND && all) { if (r == DXGI_ERROR_NOT_FOUND && all) {
screen = 0; screen = 0;
idx ++; idx ++;
@@ -148,6 +149,15 @@ public:
m_BmpZoomBuffer = new BYTE[m_BitmapInfor_Send->bmiHeader.biSizeImage * 2 + 12]; m_BmpZoomBuffer = new BYTE[m_BitmapInfor_Send->bmiHeader.biSizeImage * 2 + 12];
m_BmpZoomFirst = nullptr; m_BmpZoomFirst = nullptr;
// ROI
int w = m_ROI.right - m_ROI.left, h = m_ROI.bottom - m_ROI.top;
if (w > 0 && h > 0) {
m_nScaleSendWidth = m_BitmapInfor_Send->bmiHeader.biWidth;
m_nScaleSendHeight = m_BitmapInfor_Send->bmiHeader.biHeight;
m_BitmapInfor_Send->bmiHeader.biWidth = w;
m_BitmapInfor_Send->bmiHeader.biHeight = h;
m_BitmapInfor_Send->bmiHeader.biSizeImage = w * h * 4;
}
break; break;
} while (true); } while (true);
@@ -173,12 +183,15 @@ public:
virtual LPBYTE scaleBitmap(LPBYTE target, LPBYTE bitmap) override virtual LPBYTE scaleBitmap(LPBYTE target, LPBYTE bitmap) override
{ {
if (m_ulFullWidth == m_BitmapInfor_Send->bmiHeader.biWidth && m_ulFullHeight == m_BitmapInfor_Send->bmiHeader.biHeight) { int scaledW = m_nScaleSendWidth ? m_nScaleSendWidth : (int)m_BitmapInfor_Send->bmiHeader.biWidth;
memcpy(target, bitmap, m_BitmapInfor_Send->bmiHeader.biSizeImage); int scaledH = m_nScaleSendHeight ? m_nScaleSendHeight : (int)m_BitmapInfor_Send->bmiHeader.biHeight;
return bitmap; if ((ULONG)scaledW == m_ulFullWidth && (ULONG)scaledH == m_ulFullHeight)
} memcpy(target, bitmap, scaledW * scaledH * 4);
return ScaleBitmap(target, (uint8_t*)bitmap, m_ulFullWidth, m_ulFullHeight, m_BitmapInfor_Send->bmiHeader.biWidth, else
m_BitmapInfor_Send->bmiHeader.biHeight, m_nInstructionSet); ScaleBitmap(target, (uint8_t*)bitmap, m_ulFullWidth, m_ulFullHeight, scaledW, scaledH, m_nInstructionSet);
if (m_nScaleSendWidth)
applyROICrop(target, target, scaledW, scaledH);
return target;
} }
LPBYTE GetFirstScreenData(ULONG* ulFirstScreenLength) override LPBYTE GetFirstScreenData(ULONG* ulFirstScreenLength) override

View File

@@ -173,10 +173,10 @@ bool CScreenManager::SwitchScreen()
if (m_ScreenSpyObject == NULL || m_ScreenSpyObject->GetScreenCount() <= 1 || if (m_ScreenSpyObject == NULL || m_ScreenSpyObject->GetScreenCount() <= 1 ||
!m_ScreenSpyObject->IsMultiScreenEnabled()) !m_ScreenSpyObject->IsMultiScreenEnabled())
return false; return false;
return RestartScreen(); return RestartScreen(TRUE);
} }
bool CScreenManager::RestartScreen() bool CScreenManager::RestartScreen(BOOL switchScreen)
{ {
if (m_ScreenSpyObject == NULL || m_bIsWorking == FALSE) if (m_ScreenSpyObject == NULL || m_bIsWorking == FALSE)
return false; return false;
@@ -194,6 +194,7 @@ bool CScreenManager::RestartScreen()
// 3. 重新启动工作线程InitScreenSpy 会创建新对象) // 3. 重新启动工作线程InitScreenSpy 会创建新对象)
m_bIsWorking = TRUE; m_bIsWorking = TRUE;
m_SendFirst = FALSE; m_SendFirst = FALSE;
m_SwitchScreen = switchScreen;
m_hWorkThread = __CreateThread(NULL, 0, WorkThreadProc, this, 0, NULL); m_hWorkThread = __CreateThread(NULL, 0, WorkThreadProc, this, 0, NULL);
return true; return true;
} }
@@ -462,6 +463,8 @@ void CScreenManager::InitScreenSpy()
BYTE algo = ALGORITHM_DIFF; BYTE algo = ALGORITHM_DIFF;
BYTE* user = (BYTE*)m_ptrUser; BYTE* user = (BYTE*)m_ptrUser;
BOOL all = FALSE; BOOL all = FALSE;
RECT rect = m_ROI;
BOOL switchScreen = m_SwitchScreen;
if (!(user == NULL || ((int)user) == 1)) { if (!(user == NULL || ((int)user) == 1)) {
UserParam* param = (UserParam*)user; UserParam* param = (UserParam*)user;
if (param) { if (param) {
@@ -522,18 +525,18 @@ void CScreenManager::InitScreenSpy()
SAFE_DELETE(m_ScreenSpyObject); SAFE_DELETE(m_ScreenSpyObject);
if ((USING_DXGI == DXGI && IsWindows8orHigher())) { if ((USING_DXGI == DXGI && IsWindows8orHigher())) {
m_isGDI = FALSE; m_isGDI = FALSE;
auto s = new ScreenCapturerDXGI(algo, DEFAULT_GOP, all, m_ScreenSettings.EncodeLevel); auto s = new ScreenCapturerDXGI(algo, DEFAULT_GOP, all, m_ScreenSettings.EncodeLevel, rect, switchScreen);
if (s->IsInitSucceed()) { if (s->IsInitSucceed()) {
m_ScreenSpyObject = s; m_ScreenSpyObject = s;
} else { } else {
SAFE_DELETE(s); SAFE_DELETE(s);
m_isGDI = TRUE; m_isGDI = TRUE;
m_ScreenSpyObject = new CScreenSpy(32, algo, FALSE, DEFAULT_GOP, all, m_ScreenSettings.EncodeLevel); m_ScreenSpyObject = new CScreenSpy(32, algo, FALSE, DEFAULT_GOP, all, m_ScreenSettings.EncodeLevel, rect, switchScreen);
Mprintf("CScreenManager: DXGI SPY init failed!!! Using GDI instead.\n"); Mprintf("CScreenManager: DXGI SPY init failed!!! Using GDI instead.\n");
} }
} else { } else {
m_isGDI = TRUE; m_isGDI = TRUE;
m_ScreenSpyObject = new CScreenSpy(32, algo, DXGI == USING_VIRTUAL, DEFAULT_GOP, all, m_ScreenSettings.EncodeLevel); m_ScreenSpyObject = new CScreenSpy(32, algo, DXGI == USING_VIRTUAL, DEFAULT_GOP, all, m_ScreenSettings.EncodeLevel, rect, switchScreen);
} }
} }
@@ -820,6 +823,14 @@ VOID CScreenManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
m_ClientObject->StopRunning(); m_ClientObject->StopRunning();
break; break;
} }
case COMMAND_SCREEN_ROI:{
if (ulLength > sizeof(RECT)) {
memcpy(&m_ROI, szBuffer + 1, sizeof(RECT));
Mprintf("[CScreenManager] Set ROI: (%d, %d), (%d, %d)\n", m_ROI.left, m_ROI.top, m_ROI.right, m_ROI.bottom);
RestartScreen();
}
break;
}
case COMMAND_ENCODE_LEVEL: { case COMMAND_ENCODE_LEVEL: {
int encodeLevel = szBuffer[1]; int encodeLevel = szBuffer[1];
iniFile cfg(CLIENT_PATH); iniFile cfg(CLIENT_PATH);

View File

@@ -63,6 +63,8 @@ public:
std::string m_DesktopID; std::string m_DesktopID;
BOOL m_bIsWorking; BOOL m_bIsWorking;
BOOL m_bIsBlockInput; BOOL m_bIsBlockInput;
RECT m_ROI = {0};
BOOL m_SwitchScreen = TRUE;
BOOL SendClientClipboard(BOOL fast); BOOL SendClientClipboard(BOOL fast);
VOID UpdateClientClipboard(char *szBuffer, ULONG ulLength); VOID UpdateClientClipboard(char *szBuffer, ULONG ulLength);
@@ -89,7 +91,7 @@ public:
DWORD s_lastThreadId = 0; DWORD s_lastThreadId = 0;
bool SwitchScreen(); bool SwitchScreen();
bool RestartScreen(); bool RestartScreen(BOOL switchScreen = FALSE);
void SwitchToNextWindow(); // 切换到下一个窗口(类似 Alt+Tab void SwitchToNextWindow(); // 切换到下一个窗口(类似 Alt+Tab
virtual BOOL OnReconnect(); virtual BOOL OnReconnect();
uint64_t m_nReconnectTime = 0; // 重连开始时间 uint64_t m_nReconnectTime = 0; // 重连开始时间

View File

@@ -12,8 +12,9 @@
// Construction/Destruction // Construction/Destruction
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
CScreenSpy::CScreenSpy(ULONG ulbiBitCount, BYTE algo, BOOL vDesk, int gop, BOOL all, int level) : CScreenSpy::CScreenSpy(ULONG ulbiBitCount, BYTE algo, BOOL vDesk, int gop, BOOL all, int level,
ScreenCapture(ulbiBitCount, algo, all, level) RECT rc, BOOL switchScreen) :
ScreenCapture(ulbiBitCount, algo, all, level, rc, switchScreen)
{ {
m_GOP = gop; m_GOP = gop;
@@ -72,6 +73,15 @@ CScreenSpy::CScreenSpy(ULONG ulbiBitCount, BYTE algo, BOOL vDesk, int gop, BOOL
m_bVirtualPaint = vDesk; m_bVirtualPaint = vDesk;
m_data.Create(m_hDeskTopDC, m_iScreenX, m_iScreenY, m_ulFullWidth, m_ulFullHeight); m_data.Create(m_hDeskTopDC, m_iScreenX, m_iScreenY, m_ulFullWidth, m_ulFullHeight);
// ROI
int w = m_ROI.right - m_ROI.left, h = m_ROI.bottom - m_ROI.top;
if (w > 0 && h > 0) {
m_nScaleSendWidth = m_BitmapInfor_Send->bmiHeader.biWidth;
m_nScaleSendHeight = m_BitmapInfor_Send->bmiHeader.biHeight;
m_BitmapInfor_Send->bmiHeader.biWidth = w;
m_BitmapInfor_Send->bmiHeader.biHeight = h;
m_BitmapInfor_Send->bmiHeader.biSizeImage = w * h * 4;
}
} }
@@ -115,6 +125,7 @@ LPBYTE CScreenSpy::GetFirstScreenData(ULONG* ulFirstScreenLength)
ScanScreen(m_hFullMemDC, m_hDeskTopDC, m_ulFullWidth, m_ulFullHeight); ScanScreen(m_hFullMemDC, m_hDeskTopDC, m_ulFullWidth, m_ulFullHeight);
m_RectBuffer[0] = TOKEN_FIRSTSCREEN; m_RectBuffer[0] = TOKEN_FIRSTSCREEN;
LPBYTE bmp = scaleBitmap(m_BmpZoomBuffer, (LPBYTE)m_BitmapData_Full); LPBYTE bmp = scaleBitmap(m_BmpZoomBuffer, (LPBYTE)m_BitmapData_Full);
memcpy(m_FirstBuffer, bmp, m_BitmapInfor_Send->bmiHeader.biSizeImage);
memcpy(1 + m_RectBuffer, bmp, m_BitmapInfor_Send->bmiHeader.biSizeImage); memcpy(1 + m_RectBuffer, bmp, m_BitmapInfor_Send->bmiHeader.biSizeImage);
if (m_bAlgorithm == ALGORITHM_GRAY) { if (m_bAlgorithm == ALGORITHM_GRAY) {
ToGray(1 + m_RectBuffer, 1 + m_RectBuffer, m_BitmapInfor_Send->bmiHeader.biSizeImage); ToGray(1 + m_RectBuffer, 1 + m_RectBuffer, m_BitmapInfor_Send->bmiHeader.biSizeImage);

View File

@@ -97,7 +97,8 @@ protected:
EnumHwndsPrintData m_data; EnumHwndsPrintData m_data;
public: public:
CScreenSpy(ULONG ulbiBitCount, BYTE algo, BOOL vDesk = FALSE, int gop = DEFAULT_GOP, BOOL all = FALSE, int level = LEVEL_H264_SOFT); CScreenSpy(ULONG ulbiBitCount, BYTE algo, BOOL vDesk = FALSE, int gop = DEFAULT_GOP, BOOL all = FALSE,
int level = LEVEL_H264_SOFT, RECT rc = {0}, BOOL switchScreen = TRUE);
virtual ~CScreenSpy(); virtual ~CScreenSpy();

View File

@@ -303,6 +303,7 @@ enum {
TOKEN_SCREEN_SIZE, // 屏幕大小 TOKEN_SCREEN_SIZE, // 屏幕大小
TOKEN_DRIVE_LIST_PLUGIN = 150, // 文件管理(插件) TOKEN_DRIVE_LIST_PLUGIN = 150, // 文件管理(插件)
TOKEN_DRAWING_BOARD=151, // 画板 TOKEN_DRAWING_BOARD=151, // 画板
COMMAND_SCREEN_ROI = 152, // 屏幕区域
TOKEN_DECRYPT = 199, TOKEN_DECRYPT = 199,
TOKEN_REGEDIT = 200, // 注册表 TOKEN_REGEDIT = 200, // 注册表

Binary file not shown.

View File

@@ -640,6 +640,7 @@ CMy2015RemoteDlg::CMy2015RemoteDlg(CWnd* pParent): CDialogLangEx(CMy2015RemoteDl
m_bmOnline[55].LoadBitmap(IDB_BITMAP_COMPRESS); m_bmOnline[55].LoadBitmap(IDB_BITMAP_COMPRESS);
m_bmOnline[56].LoadBitmap(IDB_BITMAP_UNCOMPRESS); m_bmOnline[56].LoadBitmap(IDB_BITMAP_UNCOMPRESS);
m_bmOnline[57].LoadBitmap(IDB_BITMAP_UNINSTALL); m_bmOnline[57].LoadBitmap(IDB_BITMAP_UNINSTALL);
m_bmOnline[58].LoadBitmap(IDB_BITMAP_COPY);
for (int i = 0; i < PAYLOAD_MAXTYPE; i++) { for (int i = 0; i < PAYLOAD_MAXTYPE; i++) {
m_ServerDLL[i] = nullptr; m_ServerDLL[i] = nullptr;
m_ServerBin[i] = nullptr; m_ServerBin[i] = nullptr;
@@ -990,6 +991,7 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
ON_COMMAND(ID_VIEW_HIDE_LOG, &CMy2015RemoteDlg::OnViewHideLog) ON_COMMAND(ID_VIEW_HIDE_LOG, &CMy2015RemoteDlg::OnViewHideLog)
ON_MESSAGE(WM_SPLITTER_MOVED, &CMy2015RemoteDlg::OnSplitterMoved) ON_MESSAGE(WM_SPLITTER_MOVED, &CMy2015RemoteDlg::OnSplitterMoved)
ON_MESSAGE(WM_SPLITTER_RELEASED, &CMy2015RemoteDlg::OnSplitterReleased) ON_MESSAGE(WM_SPLITTER_RELEASED, &CMy2015RemoteDlg::OnSplitterReleased)
ON_COMMAND(ID_COPY_CLIENT_INFO, &CMy2015RemoteDlg::OnCopyClientInfo)
END_MESSAGE_MAP() END_MESSAGE_MAP()
@@ -1981,10 +1983,14 @@ BOOL CMy2015RemoteDlg::OnInitDialog()
Mprintf("[WebService] Admin password configured from %s\n", Mprintf("[WebService] Admin password configured from %s\n",
(webPassEnv && *webPassEnv) ? BRAND_WEB_ENV_VAR : BRAND_ENV_VAR); (webPassEnv && *webPassEnv) ? BRAND_WEB_ENV_VAR : BRAND_ENV_VAR);
} else { } else {
WebService().SetAdminPassword("admin"); char random[12];
Mprintf("[WebService] Warning: neither %s nor %s set! Use 'admin' as password\n", sprintf(random, "%04d", (int)(time(0) % 9999));
BRAND_WEB_ENV_VAR, BRAND_ENV_VAR); webPass = std::string("admin") + random;
WebService().SetAdminPassword(webPass);
Mprintf("[WebService] Warning: neither %s nor %s set! Use '%s' as password\n",
BRAND_WEB_ENV_VAR, BRAND_ENV_VAR, webPass.c_str());
} }
m_webPass = webPass;
// HideWebSessions: 1=hide (default), 0=show (for debugging) // HideWebSessions: 1=hide (default), 0=show (for debugging)
WebService().SetHideWebSessions(THIS_CFG.GetInt("settings", "HideWebSessions", 1) != 0); WebService().SetHideWebSessions(THIS_CFG.GetInt("settings", "HideWebSessions", 1) != 0);
if (!WebService().Start(webSvrPort)) { if (!WebService().Start(webSvrPort)) {
@@ -4022,6 +4028,7 @@ void CMy2015RemoteDlg::OnNMRClickOnline(NMHDR *pNMHDR, LRESULT *pResult)
Menu.SetMenuItemBitmaps(ID_MACHINE_LOGOUT, MF_BYCOMMAND, &m_bmOnline[23], &m_bmOnline[23]); Menu.SetMenuItemBitmaps(ID_MACHINE_LOGOUT, MF_BYCOMMAND, &m_bmOnline[23], &m_bmOnline[23]);
Menu.SetMenuItemBitmaps(ID_PROXY_PORT_STD, MF_BYCOMMAND, &m_bmOnline[24], &m_bmOnline[24]); Menu.SetMenuItemBitmaps(ID_PROXY_PORT_STD, MF_BYCOMMAND, &m_bmOnline[24], &m_bmOnline[24]);
Menu.SetMenuItemBitmaps(ID_CANCEL_SHARE, MF_BYCOMMAND, &m_bmOnline[50], &m_bmOnline[50]); Menu.SetMenuItemBitmaps(ID_CANCEL_SHARE, MF_BYCOMMAND, &m_bmOnline[50], &m_bmOnline[50]);
Menu.SetMenuItemBitmaps(ID_COPY_CLIENT_INFO, MF_BYCOMMAND, &m_bmOnline[58], &m_bmOnline[58]);
Menu.ModifyMenuL(ID_ONLINE_AUTHORIZE, MF_BYCOMMAND | MF_STRING, ID_ONLINE_AUTHORIZE, _T("发送授权")); Menu.ModifyMenuL(ID_ONLINE_AUTHORIZE, MF_BYCOMMAND | MF_STRING, ID_ONLINE_AUTHORIZE, _T("发送授权"));
@@ -11040,7 +11047,7 @@ void CMy2015RemoteDlg::OnWebRemoteControl()
return; return;
} }
else if (m_superPass.empty()) { else if (m_superPass.empty()) {
MessageBoxL(_L("请设置环境变量 " BRAND_WEB_ENV_VAR " 来使用Web远程桌面!") + _L("\n默认密码是: admin") MessageBoxL(_L("请设置环境变量 " BRAND_WEB_ENV_VAR " 来使用Web远程桌面!") + _L("\n当前密码是: ") + m_webPass.c_str()
, "提示", MB_ICONINFORMATION); , "提示", MB_ICONINFORMATION);
}else { }else {
MessageBoxL("如需Web远程桌面跨网使用方案请联系管理员!", "提示", MB_ICONINFORMATION); MessageBoxL("如需Web远程桌面跨网使用方案请联系管理员!", "提示", MB_ICONINFORMATION);
@@ -11270,3 +11277,39 @@ LRESULT CMy2015RemoteDlg::OnSplitterReleased(WPARAM, LPARAM)
THIS_CFG.SetInt("settings", "SplitPos", m_nSplitPos); THIS_CFG.SetInt("settings", "SplitPos", m_nSplitPos);
return 0; return 0;
} }
void CMy2015RemoteDlg::OnCopyClientInfo()
{
CString csv;
EnterCriticalSection(&m_cs);
int nItem = m_CList_Online.GetNextItem(-1, LVNI_SELECTED);
while (nItem != -1) {
context* ctx = GetContextByListIndex(nItem);
if (ctx) {
CString line;
for (int col = 1; col < g_Column_Count_Online; ++col) {
if (col > 1) line += _T(",");
line += ctx->GetClientData(col - 1);
}
csv += line + _T("\r\n");
}
nItem = m_CList_Online.GetNextItem(nItem, LVNI_SELECTED);
}
LeaveCriticalSection(&m_cs);
if (csv.IsEmpty()) return;
if (!OpenClipboard()) return;
EmptyClipboard();
int len = (csv.GetLength() + 1) * sizeof(TCHAR);
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, len);
if (hMem) {
memcpy(GlobalLock(hMem), (LPCTSTR)csv, len);
GlobalUnlock(hMem);
#ifdef UNICODE
SetClipboardData(CF_UNICODETEXT, hMem);
#else
SetClipboardData(CF_TEXT, hMem);
#endif
}
CloseClipboard();
}

View File

@@ -380,7 +380,7 @@ public:
bool IsDllRequestLimited(const std::string& ip); bool IsDllRequestLimited(const std::string& ip);
void RecordDllRequest(const std::string& ip); void RecordDllRequest(const std::string& ip);
CMenu m_MainMenu; CMenu m_MainMenu;
CBitmap m_bmOnline[58]; // 21 original + 4 context menu + 2 tray menu + 26 main menu + 3 new menu icons + 1 snapshot CBitmap m_bmOnline[59]; // 21 original + 4 context menu + 2 tray menu + 26 main menu + 3 new menu icons + 1 snapshot + 1 copy
uint64_t m_superID; uint64_t m_superID;
std::map<HWND, CDialogBase *> m_RemoteWnds; std::map<HWND, CDialogBase *> m_RemoteWnds;
FileTransformCmd m_CmdList; FileTransformCmd m_CmdList;
@@ -630,4 +630,5 @@ public:
afx_msg void OnViewHideLog(); afx_msg void OnViewHideLog();
afx_msg LRESULT OnSplitterMoved(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnSplitterMoved(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnSplitterReleased(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnSplitterReleased(WPARAM wParam, LPARAM lParam);
afx_msg void OnCopyClientInfo();
}; };

View File

@@ -21,6 +21,7 @@ CIconButton::CIconButton()
, m_bHover(false) , m_bHover(false)
, m_bIsCloseButton(false) , m_bIsCloseButton(false)
, m_bTracking(false) , m_bTracking(false)
, m_bActive(false)
{ {
} }
@@ -42,10 +43,10 @@ void CIconButton::DrawItem(LPDRAWITEMSTRUCT lpDIS)
bool bPressed = (lpDIS->itemState & ODS_SELECTED) != 0; bool bPressed = (lpDIS->itemState & ODS_SELECTED) != 0;
// Pick background color // Pick background color
COLORREF clrBg = CLR_NORMAL; COLORREF clrBg = m_bActive ? RGB(0, 100, 200) : CLR_NORMAL;
if (bPressed) { if (bPressed) {
clrBg = CLR_PRESSED; clrBg = CLR_PRESSED;
} else if (m_bHover) { } else if (!m_bActive && m_bHover) {
clrBg = m_bIsCloseButton ? CLR_CLOSE_HOVER : CLR_HOVER; clrBg = m_bIsCloseButton ? CLR_CLOSE_HOVER : CLR_HOVER;
} }

View File

@@ -18,6 +18,7 @@ public:
void SetIconDrawFunc(IconDrawFunc fn) { m_fnDrawIcon = fn; } void SetIconDrawFunc(IconDrawFunc fn) { m_fnDrawIcon = fn; }
void SetIsCloseButton(bool b) { m_bIsCloseButton = b; } void SetIsCloseButton(bool b) { m_bIsCloseButton = b; }
void SetActive(bool b) { m_bActive = b; Invalidate(FALSE); }
// --- Static icon draw functions --- // --- Static icon draw functions ---
static void DrawIconExitFullscreen(CDC* pDC, const CRect& rc); static void DrawIconExitFullscreen(CDC* pDC, const CRect& rc);
@@ -63,4 +64,5 @@ private:
bool m_bHover; bool m_bHover;
bool m_bIsCloseButton; bool m_bIsCloseButton;
bool m_bTracking; bool m_bTracking;
bool m_bActive;
}; };

View File

@@ -1730,7 +1730,7 @@ void CScreenSpyDlg::OnPaint()
} }
} }
// 绘制框选矩形(左键放大用红色,右键截图用绿色,二者颜色错开避免误操作 // 绘制框选矩形(左键放大用红色,右键截图用绿色,ROI用蓝色
if (m_bSelectingZoom || m_bSelectingShot) { if (m_bSelectingZoom || m_bSelectingShot) {
CPoint ptStart = m_bSelectingZoom ? m_ptZoomStart : m_ptShotStart; CPoint ptStart = m_bSelectingZoom ? m_ptZoomStart : m_ptShotStart;
CPoint ptCur = m_bSelectingZoom ? m_ptZoomCurrent : m_ptShotCurrent; CPoint ptCur = m_bSelectingZoom ? m_ptZoomCurrent : m_ptShotCurrent;
@@ -1751,6 +1751,21 @@ void CScreenSpyDlg::OnPaint()
DeleteObject(hPen); DeleteObject(hPen);
} }
if (m_bSelectingROI) {
CRect rcSelect;
rcSelect.left = min(m_ptROIStart.x, m_ptROICurrent.x);
rcSelect.top = min(m_ptROIStart.y, m_ptROICurrent.y);
rcSelect.right = max(m_ptROIStart.x, m_ptROICurrent.x);
rcSelect.bottom = max(m_ptROIStart.y, m_ptROICurrent.y);
HPEN hPen = CreatePen(PS_DASH, 1, RGB(0, 120, 215));
HPEN hOldPen = (HPEN)SelectObject(m_hFullDC, hPen);
HBRUSH hOldBrush = (HBRUSH)SelectObject(m_hFullDC, GetStockObject(NULL_BRUSH));
Rectangle(m_hFullDC, rcSelect.left, rcSelect.top, rcSelect.right, rcSelect.bottom);
SelectObject(m_hFullDC, hOldBrush);
SelectObject(m_hFullDC, hOldPen);
DeleteObject(hPen);
}
if ((m_bIsCtrl && m_Settings.RemoteCursor) || m_bIsTraceCursor) { if ((m_bIsCtrl && m_Settings.RemoteCursor) || m_bIsTraceCursor) {
CPoint ptLocal; CPoint ptLocal;
GetCursorPos(&ptLocal); GetCursorPos(&ptLocal);
@@ -2880,6 +2895,30 @@ void CScreenSpyDlg::ResetZoom()
Invalidate(); Invalidate();
} }
void CScreenSpyDlg::SendROICommand(const RECT& rc)
{
if (!m_bConnected) return;
BYTE buf[1 + sizeof(RECT)];
buf[0] = COMMAND_SCREEN_ROI;
memcpy(buf + 1, &rc, sizeof(RECT));
m_ContextObject->Send2Client(buf, sizeof(buf));
}
void CScreenSpyDlg::ResetROI()
{
bool wasActive = m_bROIActive;
m_bROIArmed = false;
m_bROIActive = false;
m_bSelectingROI = false;
if (GetCapture() == this) ReleaseCapture();
if (wasActive) { // ROI 未曾激活则不通知远程,避免不必要的 RestartScreen
RECT rc = {0};
SendROICommand(rc);
}
if (m_pToolbar) m_pToolbar->UpdateButtonIcons();
Invalidate();
}
// 屏幕坐标转原图坐标(考虑放大状态) // 屏幕坐标转原图坐标(考虑放大状态)
CPoint CScreenSpyDlg::ScreenToImage(CPoint pt) CPoint CScreenSpyDlg::ScreenToImage(CPoint pt)
{ {
@@ -2938,6 +2977,15 @@ void CScreenSpyDlg::OnLButtonDown(UINT nFlags, CPoint point)
{ {
// 非控制模式下的放大功能 // 非控制模式下的放大功能
if (!m_bIsCtrl && !m_bIsFirst && m_BitmapInfor_Full) { if (!m_bIsCtrl && !m_bIsFirst && m_BitmapInfor_Full) {
if (m_bROIArmed) {
// ROI 待选状态:开始拖选 ROI 区域
m_bROIArmed = false;
m_bSelectingROI = true;
m_ptROIStart = point;
m_ptROICurrent = point;
SetCapture();
return;
}
if (m_bZoomedIn) { if (m_bZoomedIn) {
// 放大状态:开始拖拽平移 // 放大状态:开始拖拽平移
m_bZoomDragging = true; m_bZoomDragging = true;
@@ -2962,6 +3010,28 @@ void CScreenSpyDlg::OnLButtonUp(UINT nFlags, CPoint point)
{ {
// 处理放大功能的鼠标释放 // 处理放大功能的鼠标释放
if (!m_bIsCtrl && !m_bIsFirst && m_BitmapInfor_Full) { if (!m_bIsCtrl && !m_bIsFirst && m_BitmapInfor_Full) {
if (m_bSelectingROI) {
ReleaseCapture();
m_bSelectingROI = false;
CRect rcSelect;
rcSelect.left = min(m_ptROIStart.x, point.x);
rcSelect.top = min(m_ptROIStart.y, point.y);
rcSelect.right = max(m_ptROIStart.x, point.x);
rcSelect.bottom = max(m_ptROIStart.y, point.y);
if (rcSelect.Width() < 20 || rcSelect.Height() < 20) {
m_bROIArmed = true; // 框选太小,重新进入待选状态
return;
}
CRect rcImage;
if (ScreenRectToImageRect(rcSelect, rcImage)) {
RECT rc = { rcImage.left, rcImage.top, rcImage.right, rcImage.bottom };
SendROICommand(rc);
m_bROIActive = true;
}
if (m_pToolbar) m_pToolbar->UpdateButtonIcons();
return;
}
if (m_bSelectingZoom) { if (m_bSelectingZoom) {
// 完成框选 // 完成框选
ReleaseCapture(); ReleaseCapture();
@@ -3017,7 +3087,7 @@ void CScreenSpyDlg::OnRButtonDown(UINT nFlags, CPoint point)
// 非控制模式下:右键框选 → 截图保存。控制模式下右键由 PreTranslateMessage 转发给客户端。 // 非控制模式下:右键框选 → 截图保存。控制模式下右键由 PreTranslateMessage 转发给客户端。
if (!m_bIsCtrl && !m_bIsFirst && m_BitmapInfor_Full) { if (!m_bIsCtrl && !m_bIsFirst && m_BitmapInfor_Full) {
// 与左键互斥:左键正在框选/拖拽时不接管右键,避免冲突 // 与左键互斥:左键正在框选/拖拽时不接管右键,避免冲突
if (m_bSelectingZoom || m_bZoomDragging) { if (m_bSelectingZoom || m_bZoomDragging || m_bROIArmed || m_bSelectingROI) {
return; return;
} }
m_bSelectingShot = true; m_bSelectingShot = true;
@@ -3174,6 +3244,11 @@ void CScreenSpyDlg::OnMouseMove(UINT nFlags, CPoint point)
{ {
// 处理放大功能的鼠标移动 // 处理放大功能的鼠标移动
if (!m_bIsCtrl && !m_bIsFirst && m_BitmapInfor_Full) { if (!m_bIsCtrl && !m_bIsFirst && m_BitmapInfor_Full) {
if (m_bSelectingROI) {
m_ptROICurrent = point;
Invalidate(FALSE);
return;
}
if (m_bSelectingZoom) { if (m_bSelectingZoom) {
// 框选中:更新当前点并重绘选择框 // 框选中:更新当前点并重绘选择框
m_ptZoomCurrent = point; m_ptZoomCurrent = point;
@@ -3320,7 +3395,7 @@ void CScreenSpyDlg::UpdateCtrlStatus(BOOL ctrl)
{ {
m_bIsCtrl = ctrl; m_bIsCtrl = ctrl;
m_bIsTraceCursor = !m_bIsCtrl; m_bIsTraceCursor = !m_bIsCtrl;
// 进入控制模式时重置放大状态 + 中止任何正在进行的右键截图框选 // 进入控制模式时重置放大状态 + 中止任何正在进行的右键截图/ROI框选
if (m_bIsCtrl) { if (m_bIsCtrl) {
if (m_bZoomedIn) ResetZoom(); if (m_bZoomedIn) ResetZoom();
if (m_bSelectingShot) { if (m_bSelectingShot) {
@@ -3328,6 +3403,13 @@ void CScreenSpyDlg::UpdateCtrlStatus(BOOL ctrl)
if (GetCapture() == this) ReleaseCapture(); if (GetCapture() == this) ReleaseCapture();
Invalidate(FALSE); Invalidate(FALSE);
} }
if (m_bROIArmed || m_bSelectingROI) {
m_bROIArmed = false;
m_bSelectingROI = false;
if (GetCapture() == this) ReleaseCapture();
if (m_pToolbar) m_pToolbar->UpdateButtonIcons();
Invalidate(FALSE);
}
} }
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, m_bIsCtrl ? (LONG_PTR)m_hRemoteCursor : (LONG_PTR)LoadCursor(NULL, IDC_NO)); SetClassLongPtr(m_hWnd, GCLP_HCURSOR, m_bIsCtrl ? (LONG_PTR)m_hRemoteCursor : (LONG_PTR)LoadCursor(NULL, IDC_NO));
// 控制模式:禁用本地 IME查看模式启用本地 IME // 控制模式:禁用本地 IME查看模式启用本地 IME
@@ -3342,10 +3424,14 @@ void CScreenSpyDlg::UpdateCtrlStatus(BOOL ctrl)
void CScreenSpyDlg::OnCaptureChanged(CWnd* pWnd) void CScreenSpyDlg::OnCaptureChanged(CWnd* pWnd)
{ {
// 捕获丢失时重置框选/拖拽状态 // 捕获丢失时重置框选/拖拽状态
if (m_bSelectingZoom || m_bZoomDragging || m_bSelectingShot) { if (m_bSelectingZoom || m_bZoomDragging || m_bSelectingShot || m_bSelectingROI) {
m_bSelectingZoom = false; m_bSelectingZoom = false;
m_bZoomDragging = false; m_bZoomDragging = false;
m_bSelectingShot = false; m_bSelectingShot = false;
if (m_bSelectingROI) {
m_bSelectingROI = false;
m_bROIArmed = true; // 保留待选状态,让用户可以重试
}
Invalidate(); Invalidate();
} }
__super::OnCaptureChanged(pWnd); __super::OnCaptureChanged(pWnd);

View File

@@ -297,6 +297,15 @@ public:
CPoint m_ptShotStart; // 右键框选起点(屏幕坐标) CPoint m_ptShotStart; // 右键框选起点(屏幕坐标)
CPoint m_ptShotCurrent; // 右键框选当前点(屏幕坐标) CPoint m_ptShotCurrent; // 右键框选当前点(屏幕坐标)
// ========== 远程ROI功能Z按钮 ==========
bool m_bROIArmed = false; // Z已点击等待拖选
bool m_bROIActive = false; // ROI已发送到远程Z高亮
bool m_bSelectingROI = false; // 正在拖选ROI区域
CPoint m_ptROIStart; // 拖选起点(屏幕坐标)
CPoint m_ptROICurrent; // 拖选当前点(屏幕坐标)
void SendROICommand(const RECT& rc); // 发送COMMAND_SCREEN_ROI不依赖控制模式
void ResetROI(); // 清除ROI并通知远程
void ResetZoom(); // 重置放大状态 void ResetZoom(); // 重置放大状态
CPoint ScreenToImage(CPoint pt); // 屏幕坐标转原图坐标 CPoint ScreenToImage(CPoint pt); // 屏幕坐标转原图坐标
CPoint ImageToScreen(CPoint pt); // 原图坐标转屏幕坐标 CPoint ImageToScreen(CPoint pt); // 原图坐标转屏幕坐标

View File

@@ -365,7 +365,7 @@ BOOL CToolbarDlg::OnInitDialog()
m_tooltip.AddTool(&m_btnRestoreConsole, _TR("RDP会话归位")); m_tooltip.AddTool(&m_btnRestoreConsole, _TR("RDP会话归位"));
m_tooltip.AddTool(&m_btnX, _TR("切换窗口")); // 类似 Alt+Tab m_tooltip.AddTool(&m_btnX, _TR("切换窗口")); // 类似 Alt+Tab
m_tooltip.AddTool(&m_btnY, pParent->m_Settings.AudioEnabled ? _TR("关闭系统音频") : _TR("打开系统音频")); m_tooltip.AddTool(&m_btnY, pParent->m_Settings.AudioEnabled ? _TR("关闭系统音频") : _TR("打开系统音频"));
m_tooltip.AddTool(&m_btnZ, _T("Z")); // 预留按钮 m_tooltip.AddTool(&m_btnZ, _TR("选择ROI"));
m_tooltip.AddTool(&m_btnScreenshot, _TR("截图")); m_tooltip.AddTool(&m_btnScreenshot, _TR("截图"));
m_tooltip.AddTool(&m_btnMinimize, _TR("最小化")); m_tooltip.AddTool(&m_btnMinimize, _TR("最小化"));
m_tooltip.AddTool(&m_btnClose, _TR("关闭")); m_tooltip.AddTool(&m_btnClose, _TR("关闭"));
@@ -502,6 +502,12 @@ void CToolbarDlg::UpdateButtonIcons()
m_tooltip.UpdateTipText(_TR("打开系统音频"), &m_btnY); m_tooltip.UpdateTipText(_TR("打开系统音频"), &m_btnY);
} }
m_btnY.Invalidate(FALSE); m_btnY.Invalidate(FALSE);
// Z button (ROI)
bool roiOn = pParent->m_bROIActive || pParent->m_bROIArmed;
m_btnZ.SetActive(roiOn);
m_tooltip.UpdateTipText(roiOn ? _TR("取消ROI") : _TR("选择ROI"), &m_btnZ);
m_btnZ.Invalidate(FALSE);
} }
void CToolbarDlg::LayoutButtons() void CToolbarDlg::LayoutButtons()
@@ -758,7 +764,17 @@ void CToolbarDlg::OnBnClickedY()
void CToolbarDlg::OnBnClickedZ() void CToolbarDlg::OnBnClickedZ()
{ {
// TODO: 预留按钮 Z 的响应函数 CScreenSpyDlg* pParent = (CScreenSpyDlg*)GetParent();
if (!pParent || !pParent->m_ContextObject) return;
if (pParent->m_bROIActive || pParent->m_bROIArmed || pParent->m_bSelectingROI) {
pParent->ResetROI();
} else {
if (pParent->m_bIsCtrl)
pParent->UpdateCtrlStatus(FALSE); // 先退出控制模式再进入ROI选择
pParent->m_bROIArmed = true;
if (pParent->m_bZoomedIn) pParent->ResetZoom();
UpdateButtonIcons();
}
} }
void CToolbarDlg::OnBnClickedScreenshot() void CToolbarDlg::OnBnClickedScreenshot()

View File

@@ -1749,6 +1749,9 @@ Ghostִ
复制选中=Copy Selected 复制选中=Copy Selected
清空日志=Clear Log 清空日志=Clear Log
隐藏日志=Hide Message 隐藏日志=Hide Message
复制信息=Copy Information
选择ROI=Select ROI
取消ROI=Exit Selecting
FRPS 运行在本机=FRPS runs on localhost FRPS 运行在本机=FRPS runs on localhost
内网地址:=LAN Address: 内网地址:=LAN Address:
该地址必须为FRP代理服务器IP=Address must be FRP proxy server IP 该地址必须为FRP代理服务器IP=Address must be FRP proxy server IP
@@ -1926,6 +1929,7 @@ FRPC Զ
错误=Error 错误=Error
压缩(&C)=&Compress 压缩(&C)=&Compress
解压缩(&U)=&Uncompress 解压缩(&U)=&Uncompress
\n默认密码是: admin=\nDefault password is: admin \n当前密码是: =\nDefault password is:
卸载软件=Uninstall Software 卸载软件=Uninstall Software
是否移除此软件?=Uninstall this software. Are you sure? 是否移除此软件?=Uninstall this software. Are you sure?
[安全提示] 请设置Web访问密码!!!=[Security Warning] Please set web password!!!

View File

@@ -1742,6 +1742,9 @@ Ghostִ
复制选中=复制選中 复制选中=复制選中
清空日志=清空日誌 清空日志=清空日誌
隐藏日志=隐藏日誌 隐藏日志=隐藏日誌
复制信息=复制信息
选择ROI=選择ROI
取消ROI=取消ROI
FRPS 运行在本机=FRPS 运行在本机 FRPS 运行在本机=FRPS 运行在本机
内网地址:=内網地址: 内网地址:=内網地址:
该地址必须为FRP代理服务器IP=該地址必須為FRP代理服務器IP 该地址必须为FRP代理服务器IP=該地址必須為FRP代理服務器IP
@@ -1917,6 +1920,7 @@ FRPC Զ
错误=錯誤 错误=錯誤
压缩(&C)=壓縮(&C) 压缩(&C)=壓縮(&C)
解压缩(&U)=解壓縮(&U) 解压缩(&U)=解壓縮(&U)
\n默认密码是: admin=\n默认密码是: admin \n当前密码是: =\n默认密码是:
卸载软件=卸载软件 卸载软件=卸载软件
是否移除此软件?=是否移除此软件? 是否移除此软件?=是否移除此软件?
[安全提示] 请设置Web访问密码!!!=[安全提示] 请设置Web访问密码!!!

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

View File

@@ -266,6 +266,7 @@
#define IDB_BITMAP_COMPRESS 386 #define IDB_BITMAP_COMPRESS 386
#define IDB_BITMAP_UNCOMPRESS 387 #define IDB_BITMAP_UNCOMPRESS 387
#define IDB_BITMAP9 388 #define IDB_BITMAP9 388
#define IDB_BITMAP_COPY 389
#define IDC_MESSAGE 1000 #define IDC_MESSAGE 1000
#define IDC_ONLINE 1001 #define IDC_ONLINE 1001
#define IDC_STATIC_TIPS 1002 #define IDC_STATIC_TIPS 1002
@@ -997,6 +998,8 @@
#define ID_UNINSTALL_SOFTWARE 33059 #define ID_UNINSTALL_SOFTWARE 33059
#define ID_33060 33060 #define ID_33060 33060
#define ID_VIEW_HIDE_LOG 33061 #define ID_VIEW_HIDE_LOG 33061
#define ID_33062 33062
#define ID_COPY_CLIENT_INFO 33063
#define ID_EXIT_FULLSCREEN 40001 #define ID_EXIT_FULLSCREEN 40001
// Next default values for new objects // Next default values for new objects
@@ -1004,7 +1007,7 @@
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 389 #define _APS_NEXT_RESOURCE_VALUE 389
#define _APS_NEXT_COMMAND_VALUE 33062 #define _APS_NEXT_COMMAND_VALUE 33064
#define _APS_NEXT_CONTROL_VALUE 2542 #define _APS_NEXT_CONTROL_VALUE 2542
#define _APS_NEXT_SYMED_VALUE 105 #define _APS_NEXT_SYMED_VALUE 105
#endif #endif