- 按方案 3.2/3.3 节以 libfreerdp3 API 内嵌,不拉起外部进程 - gdi_init 移到 PostConnect(提前调用会段错误) - 去掉 ContextSize 扩展 rdpContext,改用 freerdp* 注册表 + gfx->custom - NLA 认证传空字符串而非 nullptr,修复 SEC_E_NO_CREDENTIALS - 补充 FreeRDP3 内嵌实现笔记文档
202 lines
7.2 KiB
C++
202 lines
7.2 KiB
C++
#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
|