功能:接入 FreeRDP 3 内嵌客户端与远程桌面

变更详情:feat(kiosk-shell): FreeRDP 3 内嵌客户端跑通,登录页可进入远程桌面

- 按方案 3.2/3.3 节以 libfreerdp3 API 内嵌,不拉起外部进程
- gdi_init 移到 PostConnect(提前调用会段错误)
- 去掉 ContextSize 扩展 rdpContext,改用 freerdp* 注册表 + gfx->custom
- NLA 认证传空字符串而非 nullptr,修复 SEC_E_NO_CREDENTIALS
- 补充 FreeRDP3 内嵌实现笔记文档
This commit is contained in:
songyiming committed 2026-10-23 18:57:05 +08:00
1 parent c25bc2908a
commit 24529b80b0
8 files changed
+366 -152

No files matched your search

+46 -16
View File
@@ -14,23 +14,52 @@
#include <QTimer>
#include <QVBoxLayout>
#include <freerdp/codec/color.h>
#include "login_page.h"
namespace vdi {
namespace {
// FreeRDP 像素格式 -> QImage::Format
// 注意:FreeRDP 的像素格式值是计算宏(FREERDP_PIXEL_FORMAT),
// 不能硬编码数值,必须用宏直接比较。
// FreeRDP 主缓冲是 4 字节/像素(BGRX / BGRA / RGBA),
// QImage 没有对应的 4 字节 BGRA 格式,因此统一转成 RGB888 输出。
QImage::Format toQImageFormat(uint32_t freerdpFormat) {
switch (freerdpFormat) {
case 0x00000020: // PIXEL_FORMAT_BGRX8888
return QImage::Format_RGB888;
case 0x00000030: // PIXEL_FORMAT_BGRA8888
return QImage::Format_RGBA8888;
case 0x00000000: // PIXEL_FORMAT_RGBX8888
return QImage::Format_RGBX8888;
default:
return QImage::Format_RGBA8888;
(void)freerdpFormat;
return QImage::Format_RGB888;
}
// 把 FreeRDP 的 4 字节/像素缓冲转成 QImage 可用的 RGB888
// bytesPerLine 是 FreeRDP 的每行字节数(width*4)
QImage convertFrame(const uint8_t* data, uint32_t width, uint32_t height, uint32_t bytesPerLine,
uint32_t freerdpFormat) {
if (!data || width == 0 || height == 0) {
return QImage();
}
const bool bgr = (freerdpFormat == PIXEL_FORMAT_BGRX32) ||
(freerdpFormat == PIXEL_FORMAT_BGRA32);
QImage out(static_cast<int>(width), static_cast<int>(height), QImage::Format_RGB888);
for (uint32_t y = 0; y < height; ++y) {
const uint8_t* src = data + static_cast<size_t>(y) * bytesPerLine;
uint8_t* dst = out.scanLine(static_cast<int>(y));
for (uint32_t x = 0; x < width; ++x) {
const uint8_t* p = src + static_cast<size_t>(x) * 4;
if (bgr) {
dst[0] = p[2];
dst[1] = p[1];
dst[2] = p[0];
} else {
dst[0] = p[0];
dst[1] = p[1];
dst[2] = p[2];
}
dst += 3;
}
}
return out;
}
constexpr int kReconnectDelayMs = 3000;
@@ -39,8 +68,8 @@ constexpr int kMaxReconnectAttempts = 5;
} // namespace
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) {
fsm_ = StateMachine([this](State from, State to, const std::string& detail) {
onStateChanged(from, to, QString::fromStdString(detail));
fsm_ = StateMachine([this](State from, State to, const QString& detail) {
onStateChanged(from, to, detail);
});
setupUi();
setupHotkey();
@@ -266,11 +295,12 @@ void MainWindow::onFrame(const uint8_t* data, uint32_t width, uint32_t height, u
if (!data || width == 0 || height == 0) {
return;
}
const QImage::Format fmt = toQImageFormat(format);
// FreeRDP 的 primary_buffer 是 BGRA/BGRX 排列,QImage 需要拷贝
QImage image(reinterpret_cast<const uchar*>(data), static_cast<int>(width),
static_cast<int>(height), static_cast<int>(stride), fmt);
lastFrame_ = image.copy(); // 拷贝,脱离 FreeRDP 缓冲生命周期
// FreeRDP 主缓冲是 4 字节/像素,需转换后交给 Qt
QImage image = convertFrame(data, width, height, stride, format);
if (image.isNull()) {
return;
}
lastFrame_ = image;
frameDirty_ = true;
if (sessionView_) {