Files
SimpleRemoter/linux/ScreenHandler.h
2026-02-04 21:03:53 +01:00

890 lines
32 KiB
C++
Raw 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 "common/commands.h"
#include "client/IOCPClient.h"
#include <dlfcn.h>
#include <thread>
#include <atomic>
#include <vector>
#include <cstring>
#include <stdexcept>
// Linux 端 BITMAPINFOHEADER 定义,与 Windows 完全一致
#pragma pack(push, 1)
struct BITMAPINFOHEADER_LNX {
uint32_t biSize; // 40
int32_t biWidth;
int32_t biHeight;
uint16_t biPlanes; // 1
uint16_t biBitCount; // 32
uint32_t biCompression; // 0 (BI_RGB)
uint32_t biSizeImage;
int32_t biXPelsPerMeter; // 0
int32_t biYPelsPerMeter; // 0
uint32_t biClrUsed; // 0
uint32_t biClrImportant; // 0
};
#pragma pack(pop)
// Linux 本地 MSG64 定义(与 Windows MSG64 内存布局完全一致)
// 用于解析服务端发来的鼠标/键盘控制命令
#pragma pack(push, 1)
struct MSG64_LNX {
uint64_t hwnd;
uint64_t message;
uint64_t wParam;
uint64_t lParam;
uint64_t time;
int32_t pt_x;
int32_t pt_y;
};
#pragma pack(pop)
// X11 类型前向声明(避免 #include <X11/Xlib.h>
typedef struct _XDisplay Display;
typedef unsigned long XID;
typedef XID Window;
typedef XID Drawable;
typedef XID Pixmap;
typedef struct _XGC *GC;
struct XImage {
int width, height;
int xoffset;
int format;
char *data;
int byte_order;
int bitmap_unit;
int bitmap_bit_order;
int bitmap_pad;
int depth;
int bytes_per_line;
int bits_per_pixel;
unsigned long red_mask;
unsigned long green_mask;
unsigned long blue_mask;
// 后续字段省略XDestroyImage 通过函数指针释放
void *obdata;
struct funcs {
void *p[8]; // create_image, destroy_image, ...
} f;
};
// X11 错误处理(防止 BadMatch 等错误导致进程退出)
static int x11_error_handler(Display* dpy, void* evt)
{
// 忽略所有 X11 错误,由调用方检查返回值
return 0;
}
// XCreateGC 需要的 XGCValues 结构体(布局与 Xlib 一致)
struct XGCValues_LNX {
int function;
unsigned long plane_mask;
unsigned long foreground;
unsigned long background;
int line_width;
int line_style;
int cap_style;
int join_style;
int fill_style;
int fill_rule;
int arc_mode;
unsigned long tile; // Pixmap = XID
unsigned long stipple; // Pixmap = XID
int ts_x_origin;
int ts_y_origin;
unsigned long font; // Font = XID
int subwindow_mode;
};
// X11 GC 常量
#define GCSubwindowMode (1L<<15)
#define IncludeInferiors 1
// ============== Windows 消息常量(用于解析服务端控制命令)==============
#define WM_MOUSEMOVE 0x0200
#define WM_LBUTTONDOWN 0x0201
#define WM_LBUTTONUP 0x0202
#define WM_LBUTTONDBLCLK 0x0203
#define WM_RBUTTONDOWN 0x0204
#define WM_RBUTTONUP 0x0205
#define WM_RBUTTONDBLCLK 0x0206
#define WM_MBUTTONDOWN 0x0207
#define WM_MBUTTONUP 0x0208
#define WM_MBUTTONDBLCLK 0x0209
#define WM_MOUSEWHEEL 0x020A
#define WM_KEYDOWN 0x0100
#define WM_KEYUP 0x0101
#define WM_SYSKEYDOWN 0x0104
#define WM_SYSKEYUP 0x0105
// Windows 滚轮增量宏
#define GET_WHEEL_DELTA_WPARAM(wParam) ((short)((wParam) >> 16))
// X11 Bool 常量
#ifndef True
#define True 1
#endif
#ifndef False
#define False 0
#endif
// X11 按钮常量
#define Button1 1 // 左键
#define Button2 2 // 中键
#define Button3 3 // 右键
#define Button4 4 // 滚轮上
#define Button5 5 // 滚轮下
// X11 KeySym 常量(常用键)
#define XK_BackSpace 0xff08
#define XK_Tab 0xff09
#define XK_Return 0xff0d
#define XK_Escape 0xff1b
#define XK_Delete 0xffff
#define XK_Home 0xff50
#define XK_Left 0xff51
#define XK_Up 0xff52
#define XK_Right 0xff53
#define XK_Down 0xff54
#define XK_Page_Up 0xff55
#define XK_Page_Down 0xff56
#define XK_End 0xff57
#define XK_Insert 0xff63
#define XK_Shift_L 0xffe1
#define XK_Shift_R 0xffe2
#define XK_Control_L 0xffe3
#define XK_Control_R 0xffe4
#define XK_Caps_Lock 0xffe5
#define XK_Alt_L 0xffe9
#define XK_Alt_R 0xffea
#define XK_Super_L 0xffeb // Win键
#define XK_Super_R 0xffec
#define XK_F1 0xffbe
#define XK_F2 0xffbf
#define XK_F3 0xffc0
#define XK_F4 0xffc1
#define XK_F5 0xffc2
#define XK_F6 0xffc3
#define XK_F7 0xffc4
#define XK_F8 0xffc5
#define XK_F9 0xffc6
#define XK_F10 0xffc7
#define XK_F11 0xffc8
#define XK_F12 0xffc9
#define XK_Num_Lock 0xff7f
#define XK_Scroll_Lock 0xff14
#define XK_KP_0 0xffb0
#define XK_KP_1 0xffb1
#define XK_KP_2 0xffb2
#define XK_KP_3 0xffb3
#define XK_KP_4 0xffb4
#define XK_KP_5 0xffb5
#define XK_KP_6 0xffb6
#define XK_KP_7 0xffb7
#define XK_KP_8 0xffb8
#define XK_KP_9 0xffb9
#define XK_KP_Multiply 0xffaa
#define XK_KP_Add 0xffab
#define XK_KP_Subtract 0xffad
#define XK_KP_Decimal 0xffae
#define XK_KP_Divide 0xffaf
#define XK_KP_Enter 0xff8d
#define XK_Print 0xff61
#define XK_Pause 0xff13
#define XK_space 0x0020
// Windows VK 码到 X11 KeySym 的映射表
static unsigned long VKtoKeySym(unsigned int vk)
{
// 字母键 A-Z (VK 0x41-0x5A)
if (vk >= 0x41 && vk <= 0x5A)
return vk + 0x20; // 'a'-'z' 的 ASCII/KeySym
// 数字键 0-9 (VK 0x30-0x39)
if (vk >= 0x30 && vk <= 0x39)
return vk; // '0'-'9' 的 ASCII/KeySym
// 小键盘数字 VK_NUMPAD0-9 (0x60-0x69)
if (vk >= 0x60 && vk <= 0x69)
return XK_KP_0 + (vk - 0x60);
// F1-F12 (VK 0x70-0x7B)
if (vk >= 0x70 && vk <= 0x7B)
return XK_F1 + (vk - 0x70);
// 特殊键映射
switch (vk) {
case 0x08: return XK_BackSpace; // VK_BACK
case 0x09: return XK_Tab; // VK_TAB
case 0x0D: return XK_Return; // VK_RETURN
case 0x10: return XK_Shift_L; // VK_SHIFT
case 0x11: return XK_Control_L; // VK_CONTROL
case 0x12: return XK_Alt_L; // VK_MENU (Alt)
case 0x13: return XK_Pause; // VK_PAUSE
case 0x14: return XK_Caps_Lock; // VK_CAPITAL
case 0x1B: return XK_Escape; // VK_ESCAPE
case 0x20: return XK_space; // VK_SPACE
case 0x21: return XK_Page_Up; // VK_PRIOR
case 0x22: return XK_Page_Down; // VK_NEXT
case 0x23: return XK_End; // VK_END
case 0x24: return XK_Home; // VK_HOME
case 0x25: return XK_Left; // VK_LEFT
case 0x26: return XK_Up; // VK_UP
case 0x27: return XK_Right; // VK_RIGHT
case 0x28: return XK_Down; // VK_DOWN
case 0x2C: return XK_Print; // VK_SNAPSHOT
case 0x2D: return XK_Insert; // VK_INSERT
case 0x2E: return XK_Delete; // VK_DELETE
case 0x5B: return XK_Super_L; // VK_LWIN
case 0x5C: return XK_Super_R; // VK_RWIN
case 0x6A: return XK_KP_Multiply; // VK_MULTIPLY
case 0x6B: return XK_KP_Add; // VK_ADD
case 0x6D: return XK_KP_Subtract; // VK_SUBTRACT
case 0x6E: return XK_KP_Decimal; // VK_DECIMAL
case 0x6F: return XK_KP_Divide; // VK_DIVIDE
case 0x90: return XK_Num_Lock; // VK_NUMLOCK
case 0x91: return XK_Scroll_Lock; // VK_SCROLL
case 0xA0: return XK_Shift_L; // VK_LSHIFT
case 0xA1: return XK_Shift_R; // VK_RSHIFT
case 0xA2: return XK_Control_L; // VK_LCONTROL
case 0xA3: return XK_Control_R; // VK_RCONTROL
case 0xA4: return XK_Alt_L; // VK_LMENU
case 0xA5: return XK_Alt_R; // VK_RMENU
// 符号键(美式键盘布局)
case 0xBA: return 0x003b; // VK_OEM_1 (;:)
case 0xBB: return 0x003d; // VK_OEM_PLUS (=+)
case 0xBC: return 0x002c; // VK_OEM_COMMA (,<)
case 0xBD: return 0x002d; // VK_OEM_MINUS (-_)
case 0xBE: return 0x002e; // VK_OEM_PERIOD (.>)
case 0xBF: return 0x002f; // VK_OEM_2 (/?)
case 0xC0: return 0x0060; // VK_OEM_3 (`~)
case 0xDB: return 0x005b; // VK_OEM_4 ([{)
case 0xDC: return 0x005c; // VK_OEM_5 (\|)
case 0xDD: return 0x005d; // VK_OEM_6 (]})
case 0xDE: return 0x0027; // VK_OEM_7 ('")
default: return 0; // 未知键
}
}
// X11 函数指针类型
typedef Display* (*fn_XOpenDisplay)(const char*);
typedef int (*fn_XCloseDisplay)(Display*);
typedef XImage* (*fn_XGetImage)(Display*, Drawable, int, int, unsigned int, unsigned int, unsigned long, int);
typedef int (*fn_XDestroyImage)(XImage*);
typedef int (*fn_XSetErrorHandler)(int (*)(Display*, void*));
typedef Pixmap (*fn_XCreatePixmap)(Display*, Drawable, unsigned int, unsigned int, unsigned int);
typedef int (*fn_XFreePixmap)(Display*, Pixmap);
typedef GC (*fn_XCreateGC)(Display*, Drawable, unsigned long, void*);
typedef int (*fn_XFreeGC)(Display*, GC);
typedef int (*fn_XCopyArea)(Display*, Drawable, Drawable, GC, int, int, unsigned int, unsigned int, int, int);
typedef int (*fn_XDefaultDepth)(Display*, int);
typedef int (*fn_XSync)(Display*, int);
typedef unsigned long (*fn_XKeysymToKeycode)(Display*, unsigned long);
typedef int (*fn_XFlush)(Display*);
typedef int (*fn_XClearArea)(Display*, Window, int, int, unsigned int, unsigned int, int);
// XTest 扩展函数指针类型(用于模拟鼠标/键盘输入)
typedef int (*fn_XTestFakeMotionEvent)(Display*, int, int, int, unsigned long);
typedef int (*fn_XTestFakeButtonEvent)(Display*, unsigned int, int, unsigned long);
typedef int (*fn_XTestFakeKeyEvent)(Display*, unsigned int, int, unsigned long);
// X11 动态加载包装
class X11Loader
{
public:
fn_XOpenDisplay pXOpenDisplay;
fn_XCloseDisplay pXCloseDisplay;
fn_XGetImage pXGetImage;
fn_XDestroyImage pXDestroyImage;
// Xlib 宏的替代:通过偏移读取 Display 内部结构
// 这些函数通过 dlsym 获取
typedef int (*fn_XDefaultScreen)(Display*);
typedef int (*fn_XDisplayWidth)(Display*, int);
typedef int (*fn_XDisplayHeight)(Display*, int);
typedef Window (*fn_XRootWindow)(Display*, int);
fn_XDefaultScreen pXDefaultScreen;
fn_XDisplayWidth pXDisplayWidth;
fn_XDisplayHeight pXDisplayHeight;
fn_XRootWindow pXRootWindow;
// Pixmap 相关(解决合成窗口管理器下 XGetImage BadMatch 问题)
fn_XSetErrorHandler pXSetErrorHandler;
fn_XCreatePixmap pXCreatePixmap;
fn_XFreePixmap pXFreePixmap;
fn_XCreateGC pXCreateGC;
fn_XFreeGC pXFreeGC;
fn_XCopyArea pXCopyArea;
fn_XDefaultDepth pXDefaultDepth;
fn_XSync pXSync;
fn_XKeysymToKeycode pXKeysymToKeycode;
fn_XFlush pXFlush;
fn_XClearArea pXClearArea;
// XTest 扩展(用于模拟输入)
fn_XTestFakeMotionEvent pXTestFakeMotionEvent;
fn_XTestFakeButtonEvent pXTestFakeButtonEvent;
fn_XTestFakeKeyEvent pXTestFakeKeyEvent;
X11Loader() : m_handle(nullptr), m_xtst_handle(nullptr)
{
pXOpenDisplay = nullptr;
pXCloseDisplay = nullptr;
pXGetImage = nullptr;
pXDestroyImage = nullptr;
pXDefaultScreen = nullptr;
pXDisplayWidth = nullptr;
pXDisplayHeight = nullptr;
pXRootWindow = nullptr;
pXSetErrorHandler = nullptr;
pXCreatePixmap = nullptr;
pXFreePixmap = nullptr;
pXCreateGC = nullptr;
pXFreeGC = nullptr;
pXCopyArea = nullptr;
pXDefaultDepth = nullptr;
pXSync = nullptr;
pXKeysymToKeycode = nullptr;
pXFlush = nullptr;
pXClearArea = nullptr;
pXTestFakeMotionEvent = nullptr;
pXTestFakeButtonEvent = nullptr;
pXTestFakeKeyEvent = nullptr;
}
bool Load()
{
m_handle = dlopen("libX11.so.6", RTLD_LAZY);
if (!m_handle) m_handle = dlopen("libX11.so", RTLD_LAZY);
if (!m_handle) return false;
pXOpenDisplay = (fn_XOpenDisplay)dlsym(m_handle, "XOpenDisplay");
pXCloseDisplay = (fn_XCloseDisplay)dlsym(m_handle, "XCloseDisplay");
pXGetImage = (fn_XGetImage)dlsym(m_handle, "XGetImage");
pXDestroyImage = (fn_XDestroyImage)dlsym(m_handle, "XDestroyImage");
pXDefaultScreen = (fn_XDefaultScreen)dlsym(m_handle, "XDefaultScreen");
pXDisplayWidth = (fn_XDisplayWidth)dlsym(m_handle, "XDisplayWidth");
pXDisplayHeight = (fn_XDisplayHeight)dlsym(m_handle, "XDisplayHeight");
pXRootWindow = (fn_XRootWindow)dlsym(m_handle, "XRootWindow");
// Pixmap 相关函数
pXSetErrorHandler = (fn_XSetErrorHandler)dlsym(m_handle, "XSetErrorHandler");
pXCreatePixmap = (fn_XCreatePixmap)dlsym(m_handle, "XCreatePixmap");
pXFreePixmap = (fn_XFreePixmap)dlsym(m_handle, "XFreePixmap");
pXCreateGC = (fn_XCreateGC)dlsym(m_handle, "XCreateGC");
pXFreeGC = (fn_XFreeGC)dlsym(m_handle, "XFreeGC");
pXCopyArea = (fn_XCopyArea)dlsym(m_handle, "XCopyArea");
pXDefaultDepth = (fn_XDefaultDepth)dlsym(m_handle, "XDefaultDepth");
pXSync = (fn_XSync)dlsym(m_handle, "XSync");
pXKeysymToKeycode = (fn_XKeysymToKeycode)dlsym(m_handle, "XKeysymToKeycode");
pXFlush = (fn_XFlush)dlsym(m_handle, "XFlush");
pXClearArea = (fn_XClearArea)dlsym(m_handle, "XClearArea");
// 加载 XTest 扩展库(用于模拟鼠标/键盘输入)
m_xtst_handle = dlopen("libXtst.so.6", RTLD_LAZY);
if (!m_xtst_handle) m_xtst_handle = dlopen("libXtst.so", RTLD_LAZY);
if (m_xtst_handle) {
pXTestFakeMotionEvent = (fn_XTestFakeMotionEvent)dlsym(m_xtst_handle, "XTestFakeMotionEvent");
pXTestFakeButtonEvent = (fn_XTestFakeButtonEvent)dlsym(m_xtst_handle, "XTestFakeButtonEvent");
pXTestFakeKeyEvent = (fn_XTestFakeKeyEvent)dlsym(m_xtst_handle, "XTestFakeKeyEvent");
}
// 基本 X11 函数必须全部存在XTest 函数可选(没有时无法控制输入)
return pXOpenDisplay && pXCloseDisplay && pXGetImage && pXDestroyImage &&
pXDefaultScreen && pXDisplayWidth && pXDisplayHeight && pXRootWindow &&
pXSetErrorHandler && pXCreatePixmap && pXFreePixmap &&
pXCreateGC && pXFreeGC && pXCopyArea && pXDefaultDepth && pXSync &&
pXKeysymToKeycode && pXFlush;
}
// 检查 XTest 扩展是否可用
bool HasXTest() const
{
return pXTestFakeMotionEvent && pXTestFakeButtonEvent && pXTestFakeKeyEvent;
}
~X11Loader()
{
if (m_xtst_handle) {
dlclose(m_xtst_handle);
m_xtst_handle = nullptr;
}
if (m_handle) {
dlclose(m_handle);
m_handle = nullptr;
}
}
private:
void* m_handle;
void* m_xtst_handle;
};
class ScreenHandler : public IOCPManager
{
public:
ScreenHandler(IOCPClient* client)
: m_client(client), m_running(false), m_display(nullptr),
m_inputDisplay(nullptr),
m_width(0), m_height(0),
m_pixmap(0), m_gc(nullptr), m_xtestWarned(false)
{
if (!client) {
throw std::invalid_argument("IOCPClient pointer cannot be null");
}
// 动态加载 X11
if (!m_x11.Load()) {
throw std::runtime_error("Failed to load libX11.so (X11 not installed)");
}
// 打开 X11 Display截屏专用
m_display = m_x11.pXOpenDisplay(nullptr);
if (!m_display) {
throw std::runtime_error("Failed to open X11 display (no desktop environment?)");
}
// 打开独立的 X11 Display输入控制专用
// X11 Display 不是线程安全的,截屏线程和回调线程必须使用各自独立的连接
if (m_x11.HasXTest()) {
m_inputDisplay = m_x11.pXOpenDisplay(nullptr);
}
// 设置自定义错误处理器,防止 BadMatch 等错误导致进程退出
m_x11.pXSetErrorHandler(x11_error_handler);
// 获取默认屏幕信息
int screen = m_x11.pXDefaultScreen(m_display);
m_width = m_x11.pXDisplayWidth(m_display, screen);
m_height = m_x11.pXDisplayHeight(m_display, screen);
m_root = m_x11.pXRootWindow(m_display, screen);
// 创建离屏 Pixmap 和 GC解决合成窗口管理器下 XGetImage 的 BadMatch 问题)
int depth = m_x11.pXDefaultDepth(m_display, screen);
m_pixmap = m_x11.pXCreatePixmap(m_display, m_root, m_width, m_height, depth);
// GC 必须设置 subwindow_mode = IncludeInferiors
// 否则 XCopyArea 会裁剪掉子窗口(应用程序窗口),只拷贝 root 背景
XGCValues_LNX gcv;
memset(&gcv, 0, sizeof(gcv));
gcv.subwindow_mode = IncludeInferiors;
m_gc = m_x11.pXCreateGC(m_display, m_root, GCSubwindowMode, &gcv);
if (!m_pixmap || !m_gc) {
throw std::runtime_error("Failed to create X11 Pixmap/GC for screen capture");
}
// 初始化 BITMAPINFOHEADER
memset(&m_bmpHeader, 0, sizeof(m_bmpHeader));
m_bmpHeader.biSize = sizeof(BITMAPINFOHEADER_LNX);
m_bmpHeader.biWidth = m_width;
m_bmpHeader.biHeight = m_height;
m_bmpHeader.biPlanes = 1;
m_bmpHeader.biBitCount = 32;
m_bmpHeader.biCompression = 0; // BI_RGB
m_bmpHeader.biSizeImage = m_width * m_height * 4;
// 分配帧缓冲
m_prevFrame.resize(m_bmpHeader.biSizeImage, 0);
m_currFrame.resize(m_bmpHeader.biSizeImage, 0);
// 差异帧缓冲: token(1) + algo(1) + cursorXY(8) + cursorType(1) + 最大差异数据
m_diffBuffer.resize(1 + 1 + 8 + 1 + m_bmpHeader.biSizeImage * 2);
}
~ScreenHandler()
{
m_running = false;
if (m_captureThread.joinable()) m_captureThread.join();
if (m_inputDisplay && m_x11.pXCloseDisplay) {
m_x11.pXCloseDisplay(m_inputDisplay);
m_inputDisplay = nullptr;
}
if (m_display) {
if (m_gc && m_x11.pXFreeGC) m_x11.pXFreeGC(m_display, m_gc);
if (m_pixmap && m_x11.pXFreePixmap) m_x11.pXFreePixmap(m_display, m_pixmap);
m_pixmap = 0; m_gc = nullptr;
// 强制全屏重绘,恢复 VMware SVGA 等虚拟显卡驱动的显示状态
// XClearArea(display, window, x, y, w, h, exposures)
// w=0, h=0 表示整个窗口exposures=True 触发 Expose 事件强制所有窗口重绘
if (m_x11.pXClearArea) {
m_x11.pXClearArea(m_display, m_root, 0, 0, 0, 0, True);
}
m_x11.pXSync(m_display, 0);
if (m_x11.pXCloseDisplay) m_x11.pXCloseDisplay(m_display);
m_display = nullptr;
}
Mprintf(">>> ScreenHandler destroyed, display refreshed\n");
}
void Start()
{
bool expected = false;
if (!m_running.compare_exchange_strong(expected, true)) return;
m_captureThread = std::thread(&ScreenHandler::CaptureLoop, this);
}
// 发送 BITMAPINFOHEADER + ScreenSettings需在连接后、等待 COMMAND_NEXT 前调用)
void SendBitmapInfo()
{
const uint32_t ulLength = 1 + sizeof(BITMAPINFOHEADER_LNX) + 2 * sizeof(uint64_t) + sizeof(ScreenSettings);
std::vector<uint8_t> buf(ulLength, 0);
buf[0] = TOKEN_BITMAPINFO;
memcpy(&buf[1], &m_bmpHeader, sizeof(BITMAPINFOHEADER_LNX));
uint64_t zero = 0;
memcpy(&buf[1 + sizeof(BITMAPINFOHEADER_LNX)], &zero, sizeof(uint64_t));
memcpy(&buf[1 + sizeof(BITMAPINFOHEADER_LNX) + sizeof(uint64_t)], &zero, sizeof(uint64_t));
ScreenSettings settings = {};
settings.MaxFPS = 10;
memcpy(&buf[1 + sizeof(BITMAPINFOHEADER_LNX) + 2 * sizeof(uint64_t)], &settings, sizeof(ScreenSettings));
m_client->Send2Server((char*)buf.data(), ulLength);
}
virtual VOID OnReceive(PBYTE data, ULONG size)
{
if (!size) return;
switch (data[0]) {
case COMMAND_NEXT:
Start();
break;
case COMMAND_SCREEN_CONTROL:
// 处理鼠标/键盘控制命令
if (size >= 1 + sizeof(MSG64_LNX)) {
HandleInputEvent((MSG64_LNX*)(data + 1));
}
break;
}
}
// 处理来自服务端的鼠标/键盘输入事件
// 使用独立的 m_inputDisplay避免与截屏线程的 m_display 产生竞争
void HandleInputEvent(const MSG64_LNX* msg)
{
if (!m_inputDisplay || !m_x11.HasXTest()) {
if (!m_xtestWarned) {
Mprintf("*** XTest not available, cannot handle input ***\n");
m_xtestWarned = true;
}
return;
}
unsigned int message = (unsigned int)msg->message;
// 从 lParam 提取坐标 (MAKELPARAM 格式: low 16 bits = x, high 16 bits = y)
// 注意:不使用 pt_x/pt_y因为 Windows MSG64 结构可能有填充导致偏移不一致
int x = (int)(msg->lParam & 0xFFFF);
int y = (int)((msg->lParam >> 16) & 0xFFFF);
switch (message) {
// ================== 鼠标事件 ==================
case WM_MOUSEMOVE:
m_x11.pXTestFakeMotionEvent(m_inputDisplay, -1, x, y, 0);
m_x11.pXFlush(m_inputDisplay);
break;
case WM_LBUTTONDOWN:
m_x11.pXTestFakeMotionEvent(m_inputDisplay, -1, x, y, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button1, True, 0);
m_x11.pXFlush(m_inputDisplay);
break;
case WM_LBUTTONUP:
m_x11.pXTestFakeMotionEvent(m_inputDisplay, -1, x, y, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button1, False, 0);
m_x11.pXFlush(m_inputDisplay);
break;
case WM_LBUTTONDBLCLK:
// 双击:快速按下释放两次
m_x11.pXTestFakeMotionEvent(m_inputDisplay, -1, x, y, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button1, True, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button1, False, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button1, True, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button1, False, 0);
m_x11.pXFlush(m_inputDisplay);
break;
case WM_RBUTTONDOWN:
m_x11.pXTestFakeMotionEvent(m_inputDisplay, -1, x, y, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button3, True, 0);
m_x11.pXFlush(m_inputDisplay);
break;
case WM_RBUTTONUP:
m_x11.pXTestFakeMotionEvent(m_inputDisplay, -1, x, y, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button3, False, 0);
m_x11.pXFlush(m_inputDisplay);
break;
case WM_RBUTTONDBLCLK:
m_x11.pXTestFakeMotionEvent(m_inputDisplay, -1, x, y, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button3, True, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button3, False, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button3, True, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button3, False, 0);
m_x11.pXFlush(m_inputDisplay);
break;
case WM_MBUTTONDOWN:
m_x11.pXTestFakeMotionEvent(m_inputDisplay, -1, x, y, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button2, True, 0);
m_x11.pXFlush(m_inputDisplay);
break;
case WM_MBUTTONUP:
m_x11.pXTestFakeMotionEvent(m_inputDisplay, -1, x, y, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button2, False, 0);
m_x11.pXFlush(m_inputDisplay);
break;
case WM_MBUTTONDBLCLK:
m_x11.pXTestFakeMotionEvent(m_inputDisplay, -1, x, y, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button2, True, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button2, False, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button2, True, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, Button2, False, 0);
m_x11.pXFlush(m_inputDisplay);
break;
case WM_MOUSEWHEEL: {
short delta = GET_WHEEL_DELTA_WPARAM(msg->wParam);
// 滚轮:正值向上(Button4),负值向下(Button5)
unsigned int button = (delta > 0) ? Button4 : Button5;
int clicks = abs(delta) / 120; // 标准滚轮增量是120
if (clicks < 1) clicks = 1;
for (int i = 0; i < clicks; i++) {
m_x11.pXTestFakeButtonEvent(m_inputDisplay, button, True, 0);
m_x11.pXTestFakeButtonEvent(m_inputDisplay, button, False, 0);
}
m_x11.pXFlush(m_inputDisplay);
break;
}
// ================== 键盘事件 ==================
case WM_KEYDOWN:
case WM_SYSKEYDOWN: {
unsigned int vk = (unsigned int)msg->wParam;
unsigned long keysym = VKtoKeySym(vk);
if (keysym) {
unsigned int keycode = m_x11.pXKeysymToKeycode(m_inputDisplay, keysym);
if (keycode) {
m_x11.pXTestFakeKeyEvent(m_inputDisplay, keycode, True, 0);
m_x11.pXFlush(m_inputDisplay);
}
}
break;
}
case WM_KEYUP:
case WM_SYSKEYUP: {
unsigned int vk = (unsigned int)msg->wParam;
unsigned long keysym = VKtoKeySym(vk);
if (keysym) {
unsigned int keycode = m_x11.pXKeysymToKeycode(m_inputDisplay, keysym);
if (keycode) {
m_x11.pXTestFakeKeyEvent(m_inputDisplay, keycode, False, 0);
m_x11.pXFlush(m_inputDisplay);
}
}
break;
}
}
}
private:
IOCPClient* m_client;
std::atomic<bool> m_running;
std::thread m_captureThread;
bool m_xtestWarned;
// X11 动态加载
X11Loader m_x11;
Display* m_display; // 截屏线程专用
Display* m_inputDisplay; // 输入控制专用OnReceive 回调线程)
Window m_root;
Pixmap m_pixmap;
GC m_gc;
int m_width;
int m_height;
// 协议
BITMAPINFOHEADER_LNX m_bmpHeader;
std::vector<uint8_t> m_prevFrame;
std::vector<uint8_t> m_currFrame;
std::vector<uint8_t> m_diffBuffer;
// X11 截屏,输出 BGRA 格式(自底向上,与 BMP 一致)
// 使用 XCopyArea 将 root window 拷贝到离屏 Pixmap再对 Pixmap 调用 XGetImage
// 这样可以避免合成窗口管理器Mutter 等)导致的 BadMatch 错误
bool CaptureScreen(std::vector<uint8_t>& buffer)
{
// 先将 root window 内容拷贝到离屏 Pixmap
m_x11.pXCopyArea(m_display, m_root, m_pixmap, m_gc, 0, 0, m_width, m_height, 0, 0);
m_x11.pXSync(m_display, 0); // 等待拷贝完成
// AllPlanes = ~0UL, ZPixmap = 2
XImage* img = m_x11.pXGetImage(m_display, m_pixmap, 0, 0, m_width, m_height, ~0UL, 2);
if (!img) return false;
// X11 ZPixmap 通常是 BGRA (bits_per_pixel=32),但行序是自顶向下
// BMP 期望自底向上,需要翻转
int rowBytes = m_width * 4;
for (int y = 0; y < m_height; y++) {
int srcRow = y;
int dstRow = m_height - 1 - y; // 翻转
uint8_t* src = (uint8_t*)img->data + srcRow * img->bytes_per_line;
uint8_t* dst = buffer.data() + dstRow * rowBytes;
if (img->bits_per_pixel == 32) {
// X11 通常是 BGRX需要确保 alpha 通道
for (int x = 0; x < m_width; x++) {
dst[x * 4 + 0] = src[x * 4 + 0]; // B
dst[x * 4 + 1] = src[x * 4 + 1]; // G
dst[x * 4 + 2] = src[x * 4 + 2]; // R
dst[x * 4 + 3] = 0xFF; // A
}
} else {
// 不支持非 32 位格式
m_x11.pXDestroyImage(img);
return false;
}
}
m_x11.pXDestroyImage(img);
return true;
}
// 发送第一帧完整截图
void SendFirstScreen()
{
if (!CaptureScreen(m_currFrame)) return;
uint32_t imgSize = m_bmpHeader.biSizeImage;
std::vector<uint8_t> buf(1 + imgSize);
buf[0] = TOKEN_FIRSTSCREEN;
memcpy(&buf[1], m_currFrame.data(), imgSize);
m_client->Send2Server((char*)buf.data(), buf.size());
// 保存为上一帧
m_prevFrame = m_currFrame;
}
// 计算差异并发送 TOKEN_NEXTSCREEN
// 差异格式: 每个变化区域 = offset(4字节) + pixelCount(4字节) + BGRA 像素数据
void SendDiffFrame()
{
if (!CaptureScreen(m_currFrame)) return;
uint8_t* out = m_diffBuffer.data();
out[0] = TOKEN_NEXTSCREEN;
uint8_t* data = out + 1;
// 写入算法类型
uint8_t algo = 1; // ALGORITHM_DIFF (服务端定义为 1)
memcpy(data, &algo, sizeof(uint8_t));
// 写入光标位置 (Linux 端简单置 0)
int32_t cursorX = 0, cursorY = 0;
memcpy(data + 1, &cursorX, sizeof(int32_t));
memcpy(data + 1 + sizeof(int32_t), &cursorY, sizeof(int32_t));
// 写入光标类型
uint8_t cursorType = 0;
memcpy(data + 1 + 2 * sizeof(int32_t), &cursorType, sizeof(uint8_t));
uint32_t headerSize = 1 + 2 * sizeof(int32_t) + 1; // algo + cursor + cursorType
uint8_t* diffData = data + headerSize;
uint32_t diffLen = CompareBitmap(m_currFrame.data(), m_prevFrame.data(),
diffData, m_bmpHeader.biSizeImage);
uint32_t totalLen = 1 + headerSize + diffLen;
m_client->Send2Server((char*)out, totalLen);
// 更新上一帧
std::swap(m_prevFrame, m_currFrame);
}
// 简化版差异比较算法
// 输出格式: [byteOffset(4) + byteCount(4) + pixel data] ...
// 注意offset 和 count 都是字节数,与服务端 memcpy(dst + offset, data, count) 对应
uint32_t CompareBitmap(const uint8_t* curr, const uint8_t* prev,
uint8_t* outBuf, uint32_t totalBytes)
{
const uint32_t bytesPerPixel = 4;
const uint32_t totalPixels = totalBytes / bytesPerPixel;
const uint32_t gapThreshold = 8; // 8 像素间隙容忍
uint32_t outOffset = 0;
uint32_t i = 0;
while (i < totalPixels) {
// 跳过相同像素
while (i < totalPixels &&
*(uint32_t*)(curr + i * 4) == *(uint32_t*)(prev + i * 4)) {
i++;
}
if (i >= totalPixels) break;
// 找到变化区域的起始
uint32_t start = i;
uint32_t lastDiff = i;
// 扫描直到连续 gapThreshold 个像素相同
while (i < totalPixels) {
if (*(uint32_t*)(curr + i * 4) != *(uint32_t*)(prev + i * 4)) {
lastDiff = i;
}
else if (i - lastDiff > gapThreshold) {
break;
}
i++;
}
uint32_t end = lastDiff + 1;
uint32_t count = end - start;
uint32_t byteOffset = start * bytesPerPixel; // 字节偏移
uint32_t byteCount = count * bytesPerPixel; // 字节数
// 写入 byteOffset + byteCount + pixel data
memcpy(outBuf + outOffset, &byteOffset, sizeof(uint32_t));
outOffset += sizeof(uint32_t);
memcpy(outBuf + outOffset, &byteCount, sizeof(uint32_t));
outOffset += sizeof(uint32_t);
memcpy(outBuf + outOffset, curr + byteOffset, byteCount);
outOffset += byteCount;
}
return outOffset;
}
// 截屏主循环
void CaptureLoop()
{
Mprintf(">>> ScreenHandler CaptureLoop started (%dx%d)\n", m_width, m_height);
// 发送第一帧
SendFirstScreen();
const int fps = 10;
const int sleepMs = 1000 / fps;
while (m_running) {
clock_t start = clock();
SendDiffFrame();
int elapsed = (clock() - start) * 1000 / CLOCKS_PER_SEC;
int wait = sleepMs - elapsed;
if (wait > 0) {
usleep(wait * 1000);
}
}
Mprintf(">>> ScreenHandler CaptureLoop stopped\n");
}
};