vdi-cloud/terminal/kiosk-shell/src/rdp_session.h
宋一鸣 61127fb714 功能:实现桌面选择流程并接入 Moonlight 会话
变更详情:feat(kiosk-shell): 登录/桌面列表产品流程 + Moonlight 会话 + 看门狗进程组修复

- 登录页改为用户认证(本地演示认证,账号密码非空通过)
- 新增桌面列表页:Windows/Moonlight 可连接,Ubuntu/麒麟待分配
- 新增 DesktopService 适配层(登录/桌面列表/连接票据,后续接 API)
- 新增 MoonlightSession:QProcess 参数数组直启 moonlight-qt,退出回列表
- 状态机扩展 AUTHENTICATING / DESKTOP_LIST
- 移除开机自动 RDP,收敛为 monitor -> kiosk-shell 单一自启链
- Monitor 按进程组结束整棵进程树,修复孤儿串流进程
- vdi-moonlight 启动器新增 vsync/frame-pacing/overlay 低延迟开关
2026-10-28 20:48:12 +08:00

117 lines
4.2 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.

// RDP 会话封装(内嵌 FreeRDP 3 客户端 API)
//
// 对应《VDI 云桌面软件开发方案》3.2 / 3.3 节:
// "以 libfreerdp3 API 内嵌(非拉起外部进程),保证会话内无缝切换与全局热键"
// 协议层 FreeRDP 3;图形管线走 GFX;4K 档切 H.265 解码管线
//
// FreeRDP 3 API 结构(与 2.x 差异较大):
// freerdp* instance = freerdp_new(); // 主对象
// instance->context = <rdpContext*>; // 内部结构,公开
// instance->context->gdi // GDI 渲染状态
// instance->PostConnect / PostDisconnect // 事件回调
// instance->pClientEntryPoints // 客户端入口点
//
// 渲染:gdi_graphics_pipeline_init_ex 的 pcRdpgfxUpdateSurfaceArea 回调
// 拿到解码后的帧缓冲,交由 Qt 绘制。
// 后续 freerdp-rk 分支替换为 GStreamer + mppvideodec 零拷贝管线。
#ifndef KIOSK_SHELL_RDP_SESSION_H
#define KIOSK_SHELL_RDP_SESSION_H
#include <atomic>
#include <functional>
#include <mutex>
#include <string>
#include <thread>
#include <freerdp/freerdp.h>
#include <freerdp/gdi/gdi.h>
#include <freerdp/gdi/gfx.h>
#include <freerdp/settings.h>
namespace vdi {
struct RdpOptions {
std::string server; // 主机或 host:port
std::string username;
std::string password;
std::string domain;
uint32_t keyboardLayout = 0;
int width = 1920;
int height = 1440;
int bpp = 32;
bool fullscreen = true;
// 图形管线能力组合。FreeRDP 3.32.1 的 /gfx 不接受单个管道名,
// 必须是 "|" 分隔的组合(实测 AVC420 单独使用会解析失败)。
std::string gfx = "progressive|RFX|AVC420|AVC444";
bool ignoreCertificate = true; // POC 阶段;正式产品必须做证书校验
};
struct RdpCallbacks {
// 每帧解码完成
std::function<void(const uint8_t* data, uint32_t width, uint32_t height, uint32_t stride,
uint32_t format)>
onFrame;
// 会话状态变化
std::function<void(bool connected, const std::string& message)> onState;
};
class RdpSession {
public:
RdpSession();
~RdpSession();
RdpSession(const RdpSession&) = delete;
RdpSession& operator=(const RdpSession&) = delete;
// 启动会话(内部线程执行阻塞式连接)
bool start(const RdpOptions& options, RdpCallbacks callbacks);
// 主动断开
void stop();
bool isRunning() const { return running_.load(); }
// 向远程会话注入键盘和鼠标事件;由 GUI 线程调用。
bool sendKeyEvent(bool pressed, bool repeat, uint32_t rdpScanCode);
bool sendMouseEvent(uint16_t flags, uint16_t x, uint16_t y);
private:
void run();
void applySettings(rdpSettings* settings);
// FreeRDP 回调(静态,通过 instance->context 扩展结构找回 C++ 对象)
static BOOL onPostConnect(freerdp* instance);
static void onPostDisconnect(freerdp* instance);
// NLA 认证回调:把配置中的账号/密码交给 FreeRDP
static BOOL onAuthenticateEx(freerdp* instance, char** username, char** password, char** domain,
rdp_auth_reason reason);
// GDI 输出回调:传统位图路径的更新完成。
static BOOL onBitmapUpdate(rdpContext* context, const BITMAP_UPDATE* bitmap);
static BOOL onEndPaint(rdpContext* context);
// GFX 完整帧边界:先让 FreeRDP 完成所有 surface 合成,再提交一次。
static UINT onEndFrame(RdpgfxClientContext* context,
const RDPGFX_END_FRAME_PDU* endFrame);
// 把当前 GDI 帧缓冲交给上层
void emitFrame();
RdpOptions options_;
RdpCallbacks callbacks_;
std::atomic<bool> running_{false};
std::atomic<bool> connected_{false};
freerdp* instance_ = nullptr;
rdpContext* context_ = nullptr;
std::thread worker_;
mutable std::mutex inputMutex_;
uint32_t frameWidth_ = 0;
uint32_t frameHeight_ = 0;
uint32_t frameStride_ = 0;
uint32_t frameFormat_ = 0;
bool frameDirty_ = false;
pcRdpgfxEndFrame originalEndFrame_ = nullptr;
bool gfxPipelineActive_ = false;
};
} // namespace vdi
#endif // KIOSK_SHELL_RDP_SESSION_H