Files
SimpleRemoter/server/2015Remote/ScreenSpyDlg.h
2026-04-19 22:55:21 +02:00

333 lines
12 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include <imm.h>
#include <map>
#include "IOCPServer.h"
#include "..\..\client\CursorInfo.h"
#include "VideoDlg.h"
#include "ToolbarDlg.h"
#include "2015RemoteDlg.h"
extern "C"
{
#include "libavcodec\avcodec.h"
#include "libavutil\avutil.h"
#include "libyuv\libyuv.h"
}
#ifndef _WIN64
// https://github.com/Terodee/FFMpeg-windows-static-build/releases
#pragma comment(lib,"ffmpeg/libavcodec.lib")
#pragma comment(lib,"ffmpeg/libavutil.lib")
#pragma comment(lib,"ffmpeg/libswresample.lib")
#pragma comment(lib,"libyuv/libyuv.lib")
#else
#pragma comment(lib,"x264/libx264_x64.lib")
#pragma comment(lib,"libyuv/libyuv_x64.lib")
// https://github.com/ShiftMediaProject/FFmpeg
#pragma comment(lib,"ffmpeg/libavcodec_x64.lib")
#pragma comment(lib,"ffmpeg/libavutil_x64.lib")
#pragma comment(lib,"ffmpeg/libswresample_x64.lib")
#endif
#pragma comment(lib, "Mfplat.lib")
#pragma comment(lib, "Mfuuid.lib")
#pragma comment(lib, "Bcrypt.lib")
#pragma comment(lib, "Strmiids.lib")
// 文件接收消息(用于将工作线程的文件数据转发到主线程处理)
#define WM_RECVFILEV2_CHUNK (WM_USER + 0x200)
#define WM_RECVFILEV2_COMPLETE (WM_USER + 0x201)
// ScreenSpyDlg 系统菜单命令 ID
enum {
IDM_CONTROL = 0x1010,
IDM_FULLSCREEN,
IDM_SEND_CTRL_ALT_DEL,
IDM_TRACE_CURSOR, // 跟踪显示远程鼠标
IDM_BLOCK_INPUT, // 锁定远程计算机输入
IDM_SAVEDIB, // 保存图片
IDM_GET_CLIPBOARD, // 获取剪贴板
IDM_SET_CLIPBOARD, // 设置剪贴板
IDM_ADAPTIVE_SIZE,
IDM_SAVEAVI,
IDM_SAVEAVI_H264,
IDM_SWITCHSCREEN,
IDM_MULTITHREAD_COMPRESS,
IDM_FPS_10,
IDM_FPS_15,
IDM_FPS_20,
IDM_FPS_25,
IDM_FPS_30,
IDM_FPS_UNLIMITED,
IDM_ORIGINAL_SIZE,
IDM_SCREEN_1080P,
IDM_REMOTE_CURSOR,
IDM_SCROLL_DETECT_OFF, // 滚动检测:关闭(局域网)
IDM_SCROLL_DETECT_2, // 滚动检测:跨网推荐
IDM_SCROLL_DETECT_4, // 滚动检测:标准模式
IDM_SCROLL_DETECT_8, // 滚动检测省CPU模式
IDM_QUALITY_OFF, // 关闭质量控制(使用原有算法)
IDM_ADAPTIVE_QUALITY, // 自适应质量
IDM_QUALITY_ULTRA, // 手动质量Ultra
IDM_QUALITY_HIGH, // 手动质量High
IDM_QUALITY_GOOD, // 手动质量Good
IDM_QUALITY_MEDIUM, // 手动质量Medium
IDM_QUALITY_LOW, // 手动质量Low
IDM_QUALITY_MINIMAL, // 手动质量Minimal
IDM_ENABLE_SSE2,
IDM_FAST_STRETCH, // 快速缩放模式降低CPU占用
IDM_CUSTOM_CURSOR, // 使用自定义光标
IDM_RESTORE_CONSOLE, // RDP会话归位
IDM_RESET_VIRTUAL_DESKTOP, // 重置虚拟桌面
IDM_AUDIO_TOGGLE, // 音频开关
};
// 状态信息窗口 - 全屏时显示帧率/速度/质量
class CStatusInfoWnd : public CWnd
{
public:
CStatusInfoWnd() : m_nOpacityLevel(0), m_bVisible(false), m_bDragging(false) {}
BOOL Create(CWnd* pParent);
void UpdateInfo(double fps, double kbps, const CString& quality);
void Show();
void Hide();
void SetOpacityLevel(int level);
void UpdatePosition(const RECT& rcMonitor);
void LoadSettings();
void SaveSettings();
bool IsVisible() const { return m_bVisible; }
bool IsParentInControlMode(); // 检查父窗口是否处于控制模式
protected:
afx_msg void OnPaint();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
DECLARE_MESSAGE_MAP()
private:
CString m_strInfo;
int m_nOpacityLevel;
bool m_bVisible;
// 拖动支持
bool m_bDragging;
CPoint m_ptDragStart;
// 位置保存
double m_dOffsetXRatio = 0.5;
int m_nOffsetY = 50;
bool m_bHasCustomPosition = false;
};
// CScreenSpyDlg 对话框
class CScreenSpyDlg : public DialogBase
{
DECLARE_DYNAMIC(CScreenSpyDlg)
CToolbarDlg* m_pToolbar = nullptr;
CMy2015RemoteDlg* m_pParent = nullptr;
public:
CStatusInfoWnd* m_pStatusInfoWnd = nullptr;
// MaxFPS=20, ScrollDetectInterval=2, Reserved={}, Capabilities=0
// MaxFPS=20, CompressThread=0, ScreenStrategy=0, ScreenWidth=0, ScreenHeight=0,
// FullScreen=0, RemoteCursor=0, ScrollDetectInterval=2, QualityLevel=-1,
// CpuSpeedup=0, ScreenType=0, AudioEnabled=0, Reserved={}, Capabilities=0
ScreenSettings m_Settings = { 20, 0, 0, 0, 0, 0, 0, 2, -1, 0, 0, 0, {}, 0 };
public:
// 快速缩放模式(全局配置,所有实例共享)
static int s_nFastStretch; // -1=未初始化, 0=关闭, 1=开启
static bool GetFastStretchMode();
static void SetFastStretchMode(bool bFast);
CScreenSpyDlg(CMy2015RemoteDlg* Parent, Server* IOCPServer=NULL, CONTEXT_OBJECT *ContextObject=NULL);
virtual ~CScreenSpyDlg();
virtual BOOL ShouldReconnect()
{
return TRUE;
}
VOID SendNext(void);
VOID OnReceiveComplete();
HDC m_hFullDC;
HDC m_hFullMemDC;
HBITMAP m_BitmapHandle;
PVOID m_BitmapData_Full;
LPBITMAPINFO m_BitmapInfor_Full;
VOID DrawFirstScreen(void);
VOID DrawNextScreenDiff(bool keyFrame);
VOID DrawScrollFrame(void);
BOOL m_bIsFirst;
bool m_bQualitySwitch = false; // 质量切换中,不显示"请等待"
ULONG m_ulHScrollPos;
ULONG m_ulVScrollPos;
// fillMode: 0=不填充, 1=全黑, 2=半透明
VOID DrawTipString(CString strString, int fillMode=1);
POINT m_ClientCursorPos;
BYTE m_bCursorIndex;
BOOL m_bIsTraceCursor;
CCursorInfo m_CursorInfo; //自定义的一个系统的光标类
VOID SendCommand(const MYMSG* Msg);
void SendScaledMouseMessage(MSG* pMsg, bool makeLP);
VOID UpdateServerClipboard(char *szBuffer,ULONG ulLength);
VOID SendServerClipboard(void);
BOOL m_bIsCtrl;
LPBYTE m_szData;
BOOL m_bSend;
ULONG m_ulMsgCount;
int m_FrameID;
HIMC m_hOldIMC = NULL; // 保存原始 IME 上下文,控制模式切换时使用
bool m_bHide = false;
std::string m_strSaveNotice; // 截图保存路径提示
ULONGLONG m_nSaveNoticeTime = 0; // 截图提示开始时间
BOOL m_bUsingFRP = FALSE;
// 文件接收进度对话框(用于 Linux Ctrl+C -> 服务端 Ctrl+V
// 按 transferID 管理多个并发传输
std::map<uint64_t, class CDlgFileSend*> m_FileRecvDlgs;
void SaveSnapshot(void);
// 对话框数据
enum { IDD = IDD_DIALOG_SCREEN_SPY };
WINDOWPLACEMENT m_struOldWndpl;
const AVCodec* m_pCodec;
AVCodecContext* m_pCodecContext;
AVPacket m_AVPacket;
AVFrame m_AVFrame;
clock_t m_lastMouseMove; // 鼠标移动时间
POINT m_lastMousePoint;// 上次鼠标位置
BOOL m_bAdaptiveSize = TRUE;
HCURSOR m_hRemoteCursor = NULL;
HCURSOR m_hCustomCursor = NULL; // 缓存的自定义光标
DWORD m_dwCustomCursorHash = 0; // 当前自定义光标哈希
BOOL m_bUseCustomCursor = TRUE; // 是否使用自定义光标
CRect m_CRect;
double m_wZoom=1, m_hZoom=1;
bool m_bMouseTracking = false;
CString m_aviFile;
CBmpToAvi m_aviStream;
// 传输速率统计
ULONG m_ulBytesThisSecond = 0; // 本秒累计字节
double m_dTransferRate = 0; // 当前速率 (KB/s)
// 帧率统计 (使用EMA平滑)
ULONG m_ulFramesThisSecond = 0; // 本秒累计帧数
double m_dFrameRate = 0; // 平滑后的帧率 (FPS)
// 自适应质量
struct {
bool enabled = false; // 是否启用自适应 (默认关闭)
int currentLevel = QUALITY_HIGH; // 当前质量等级
int currentMaxWidth = 0; // 当前分辨率限制 (0=原始)
int lastRTT = 0; // 上次RTT值
ULONGLONG lastChangeTime = 0; // 上次切换时间
ULONGLONG lastResChangeTime = 0; // 上次分辨率变化时间
ULONGLONG startTime = 0; // 启动时间 (用于延迟启动自适应)
int stableCount = 0; // 稳定计数 (用于防抖)
} m_AdaptiveQuality;
volatile bool m_bResolutionChanging = false; // 分辨率切换中,阻止解码
// ========== 音频播放 ==========
// m_Settings.AudioEnabled 表示是否启用音频(与客户端同步)
BOOL m_bAudioPlaying = FALSE; // 音频是否正在播放
HWAVEOUT m_hWaveOut = NULL; // 波形输出设备句柄
WAVEFORMATEX m_AudioFormat = {}; // 音频格式
static const int AUDIO_BUFFER_COUNT = 8; // 缓冲区数量增加到8个
WAVEHDR m_WaveHdr[AUDIO_BUFFER_COUNT] = {}; // 波形头
LPBYTE m_pAudioBuf[AUDIO_BUFFER_COUNT] = {};// 音频缓冲区
int m_nAudioBufIndex = 0; // 当前缓冲区索引
static const DWORD AUDIO_BUF_SIZE = 8192; // 8KB 每个缓冲区(更小更频繁)
// 环形缓冲区(吸收网络抖动)
static const DWORD RING_BUF_SIZE = 65536; // 64KB 环形缓冲区
BYTE* m_pRingBuf = nullptr; // 环形缓冲区
DWORD m_nRingHead = 0; // 写入位置
DWORD m_nRingTail = 0; // 读取位置
DWORD m_nRingDataLen = 0; // 缓冲数据量
int m_nPrebufferCount = 0; // 预缓冲计数
static const int PREBUFFER_TARGET = 5; // 预缓冲目标积累5个包再开始播放约50ms
BYTE m_nAudioCompression = 0; // 音频压缩类型 (AudioCompression)
#if USING_OPUS
void* m_pOpusDecoder = nullptr; // Opus 解码器
short* m_pOpusDecodeBuffer = nullptr; // Opus 解码输出缓冲区
#endif
void OnAudioData(BYTE* pData, UINT32 len); // 处理音频数据
BOOL InitAudioPlayback(const AudioFormat* fmt); // 初始化音频播放
void StopAudioPlayback(); // 停止音频播放
void SendAudioCtrl(BYTE enable, BYTE persist); // 发送音频控制命令
void FeedAudioBuffers(); // 填充音频缓冲区
int GetClientRTT(); // 获取客户端RTT(ms)
void EvaluateQuality(); // 评估并调整质量
void ApplyQualityLevel(int level, bool persist = false); // 应用质量等级
const char* GetQualityName(int level); // 获取质量等级名称
void UpdateQualityMenuCheck(CMenu* SysMenu = nullptr); // 更新质量菜单勾选状态
void OnTimer(UINT_PTR nIDEvent);
void UpdateWindowTitle();
bool Decode(LPBYTE Buffer, int size);
void EnterFullScreen();
bool LeaveFullScreen();
void UpdateCtrlStatus(BOOL ctrl);
void OnDropFiles(HDROP hDropInfo);
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnMouseLeave();
afx_msg void OnKillFocus(CWnd* pNewWnd);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
afx_msg LRESULT OnDisconnect(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnWaveOutDone(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnRecvFileV2Chunk(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnRecvFileV2Complete(WPARAM wParam, LPARAM lParam);
afx_msg void OnExitFullscreen()
{
BYTE cmd[4] = { CMD_FULL_SCREEN, m_Settings.FullScreen = FALSE };
m_ContextObject->Send2Client(cmd, sizeof(cmd));
LeaveFullScreen();
}
afx_msg void OnShowStatusInfo()
{
if (m_pStatusInfoWnd) m_pStatusInfoWnd->Show();
}
afx_msg void OnHideStatusInfo()
{
if (m_pStatusInfoWnd) m_pStatusInfoWnd->Hide();
}
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
DECLARE_MESSAGE_MAP()
public:
void PrepareDrawing(const LPBITMAPINFO bmp);
virtual BOOL OnInitDialog();
afx_msg void OnClose();
afx_msg void OnPaint();
BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
virtual BOOL PreTranslateMessage(MSG* pMsg);
void OnLButtonDblClk(UINT nFlags, CPoint point);
};