vdi-cloud/terminal/kiosk-shell/src/login_page.cpp
dev 589cde457c 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 内嵌实现笔记文档
2026-10-23 18:57:05 +08:00

202 lines
7.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.

#include "login_page.h"
#include <QCheckBox>
#include <QComboBox>
#include <QFont>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QProcess>
#include <QPushButton>
#include <QVBoxLayout>
namespace vdi {
namespace {
const char* kResolutionIds[] = {"1920x1440", "2560x1440", "1920x1080", "1280x720"};
QString detectNetworkSummary() {
// 取默认路由的出接口与该接口上的 IPv4 地址
QProcess process;
process.start("sh", QStringList() << "-c" <<
"dev=$(ip -4 route show default | awk '{print $NF}' | head -n 1); "
"addr=$(ip -4 -o addr show dev \"$dev\" | awk '{print $4}' | head -n 1); "
"echo \"$dev $addr\"");
process.waitForFinished(2000);
const QString out = QString::fromUtf8(process.readAllStandardOutput()).trimmed();
if (out.isEmpty()) {
return QStringLiteral("网络:未连接");
}
const QStringList parts = out.split(' ', Qt::SkipEmptyParts);
QString iface = parts.value(0);
QString addr = parts.value(1);
if (addr.contains('/')) {
addr = addr.split('/').value(0);
}
if (iface.isEmpty()) {
return QStringLiteral("网络:未连接");
}
return QStringLiteral("网络:%1 (%2)").arg(iface, addr);
}
} // namespace
LoginPage::LoginPage(const TerminalConfig& initial, QWidget* parent)
: QWidget(parent), cfg_(initial) {
auto* root = new QVBoxLayout(this);
root->setContentsMargins(80, 60, 80, 40);
root->setSpacing(18);
// ---- 标题区 ----
auto* title = new QLabel(QStringLiteral("VDI 云终端"), this);
QFont titleFont = title->font();
titleFont.setPointSize(28);
titleFont.setBold(true);
title->setFont(titleFont);
title->setAlignment(Qt::AlignCenter);
root->addWidget(title);
auto* subtitle = new QLabel(QStringLiteral("RK3528 远程桌面接入"), this);
QFont subFont = subtitle->font();
subFont.setPointSize(13);
subtitle->setFont(subFont);
subtitle->setAlignment(Qt::AlignCenter);
subtitle->setStyleSheet(QStringLiteral("color: #8a94a6;"));
root->addWidget(subtitle);
// ---- 设备信息条 ----
auto* infoBox = new QHBoxLayout();
labelSn_ = new QLabel(QStringLiteral("设备 SN:%1").arg(QString::fromStdString(cfg_.sn)), this);
labelNetwork_ = new QLabel(detectNetworkSummary(), this);
labelSn_->setStyleSheet(QStringLiteral("color: #8a94a6;"));
labelNetwork_->setStyleSheet(QStringLiteral("color: #8a94a6;"));
infoBox->addWidget(labelSn_);
infoBox->addStretch(1);
infoBox->addWidget(labelNetwork_);
root->addLayout(infoBox);
// ---- 表单 ----
auto* grid = new QGridLayout();
grid->setHorizontalSpacing(14);
grid->setVerticalSpacing(12);
grid->setColumnStretch(1, 1);
editServer_ = new QLineEdit(QString::fromStdString(cfg_.server), this);
editServer_->setPlaceholderText(QStringLiteral("服务器地址,例如 192.168.1.70"));
editUser_ = new QLineEdit(QString::fromStdString(cfg_.username), this);
editUser_->setPlaceholderText(QStringLiteral("登录账号"));
editPassword_ = new QLineEdit(QString::fromStdString(cfg_.password), this);
editPassword_->setEchoMode(QLineEdit::Password);
editPassword_->setPlaceholderText(QStringLiteral("登录密码(可留空)"));
comboResolution_ = new QComboBox(this);
for (const char* id : kResolutionIds) {
comboResolution_->addItem(QString::fromLatin1(id), QString::fromLatin1(id));
}
int resIndex = comboResolution_->findData(QString::fromStdString(cfg_.resolution));
comboResolution_->setCurrentIndex(resIndex >= 0 ? resIndex : 0);
checkRemember_ = new QCheckBox(QStringLiteral("记住连接参数"), this);
checkRemember_->setChecked(cfg_.remember);
struct Row {
QString label;
QWidget* widget;
};
const Row rows[] = {
{QStringLiteral("服务器地址"), editServer_},
{QStringLiteral("登录账号"), editUser_},
{QStringLiteral("登录密码"), editPassword_},
{QStringLiteral("显示分辨率"), comboResolution_},
};
int row = 0;
for (const Row& r : rows) {
auto* label = new QLabel(r.label, this);
label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
grid->addWidget(label, row, 0);
grid->addWidget(r.widget, row, 1);
++row;
}
grid->addWidget(checkRemember_, row, 1);
root->addLayout(grid);
// ---- 按钮 ----
auto* buttonRow = new QHBoxLayout();
buttonRow->addStretch(1);
buttonConnect_ = new QPushButton(QStringLiteral("连接"), this);
buttonConnect_->setMinimumSize(160, 46);
buttonConnect_->setStyleSheet(
QStringLiteral("QPushButton { background: #2f6feb; color: white; border: none; "
"border-radius: 6px; font-weight: bold; }"
"QPushButton:disabled { background: #3a4a63; }"));
buttonConnect_->setDefault(true);
buttonQuit_ = new QPushButton(QStringLiteral("退出"), this);
buttonQuit_->setMinimumSize(110, 46);
buttonRow->addWidget(buttonConnect_);
buttonRow->addWidget(buttonQuit_);
buttonRow->addStretch(1);
root->addLayout(buttonRow);
// ---- 状态条 ----
labelStatus_ = new QLabel(QStringLiteral("就绪"), this);
labelStatus_->setAlignment(Qt::AlignCenter);
labelStatus_->setStyleSheet(QStringLiteral("color: #8a94a6;"));
root->addWidget(labelStatus_);
root->addStretch(1);
connect(buttonConnect_, &QPushButton::clicked, this, &LoginPage::onConnectClicked);
connect(buttonQuit_, &QPushButton::clicked, this, [this]() { emit quitRequested(); });
}
void LoginPage::setConnectHandler(ConnectHandler handler) {
connectHandler_ = std::move(handler);
}
void LoginPage::setStatus(const QString& text, bool error) {
labelStatus_->setText(text);
labelStatus_->setStyleSheet(
QStringLiteral("color: %1;").arg(error ? QStringLiteral("#e05561")
: QStringLiteral("#8a94a6")));
}
void LoginPage::setBusy(bool busy) {
buttonConnect_->setEnabled(!busy);
buttonConnect_->setText(busy ? QStringLiteral("连接中…") : QStringLiteral("连接"));
editServer_->setEnabled(!busy);
editUser_->setEnabled(!busy);
editPassword_->setEnabled(!busy);
comboResolution_->setEnabled(!busy);
}
TerminalConfig LoginPage::currentConfig() const {
TerminalConfig out = cfg_;
out.server = editServer_->text().trimmed().toStdString();
out.username = editUser_->text().trimmed().toStdString();
out.password = editPassword_->text().toStdString();
out.resolution = comboResolution_->currentData().toString().toStdString();
out.remember = checkRemember_->isChecked();
return out;
}
void LoginPage::onConnectClicked() {
TerminalConfig cfg = currentConfig();
if (cfg.server.empty()) {
setStatus(QStringLiteral("请填写服务器地址"), true);
return;
}
setBusy(true);
setStatus(QStringLiteral("正在连接 %1 …").arg(QString::fromStdString(cfg.server)));
if (cfg.remember) {
saveConfig(cfg);
}
if (connectHandler_) {
connectHandler_(cfg);
}
emit connectRequested(cfg);
}
} // namespace vdi