功能:接入 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:
1 parent
c25bc2908a
commit
24529b80b0
8 files changed
+366
-152
No files matched your search
@@ -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_) {
|
||||
|
||||
Reference in new issue
Block a user