Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "session_tab.h"
|
||||
|
||||
#include "rdp_display_widget.h"
|
||||
#include "session_backend_factory.h"
|
||||
#include "terminal_view.h"
|
||||
|
||||
@@ -62,6 +63,7 @@ SessionTab::SessionTab(const Profile& profile, QWidget* parent)
|
||||
m_state(SessionState::Disconnected),
|
||||
m_terminalThemeName(QStringLiteral("Dark")),
|
||||
m_sshTerminal(nullptr),
|
||||
m_rdpDisplay(nullptr),
|
||||
m_terminalOutput(nullptr),
|
||||
m_eventLog(nullptr),
|
||||
m_toggleEventsButton(nullptr),
|
||||
@@ -148,6 +150,26 @@ SessionTab::SessionTab(const Profile& profile, QWidget* parent)
|
||||
m_backend,
|
||||
&SessionBackend::updateTerminalSize,
|
||||
Qt::QueuedConnection);
|
||||
connect(this,
|
||||
&SessionTab::requestKeyEvent,
|
||||
m_backend,
|
||||
&SessionBackend::sendKeyEvent,
|
||||
Qt::QueuedConnection);
|
||||
connect(this,
|
||||
&SessionTab::requestMouseMoveEvent,
|
||||
m_backend,
|
||||
&SessionBackend::sendMouseMoveEvent,
|
||||
Qt::QueuedConnection);
|
||||
connect(this,
|
||||
&SessionTab::requestMouseButtonEvent,
|
||||
m_backend,
|
||||
&SessionBackend::sendMouseButtonEvent,
|
||||
Qt::QueuedConnection);
|
||||
connect(this,
|
||||
&SessionTab::requestMouseWheelEvent,
|
||||
m_backend,
|
||||
&SessionBackend::sendMouseWheelEvent,
|
||||
Qt::QueuedConnection);
|
||||
|
||||
connect(m_backend,
|
||||
&SessionBackend::stateChanged,
|
||||
@@ -174,6 +196,24 @@ SessionTab::SessionTab(const Profile& profile, QWidget* parent)
|
||||
this,
|
||||
&SessionTab::onBackendHostKeyConfirmationRequested,
|
||||
Qt::QueuedConnection);
|
||||
connect(m_backend,
|
||||
&SessionBackend::frameUpdated,
|
||||
this,
|
||||
[this](const QImage& frame) {
|
||||
if (m_rdpDisplay != nullptr) {
|
||||
m_rdpDisplay->setFrame(frame);
|
||||
}
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
connect(m_backend,
|
||||
&SessionBackend::remoteDesktopSizeChanged,
|
||||
this,
|
||||
[this](int width, int height) {
|
||||
if (m_rdpDisplay != nullptr) {
|
||||
m_rdpDisplay->setRemoteDesktopSize(width, height);
|
||||
}
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
m_backendThread->start();
|
||||
}
|
||||
@@ -284,6 +324,12 @@ void SessionTab::clearTerminal()
|
||||
emit requestInput(QStringLiteral("\x0c"));
|
||||
}
|
||||
m_terminalOutput->setFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_rdpDisplay != nullptr) {
|
||||
m_rdpDisplay->clearFrame();
|
||||
m_rdpDisplay->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,6 +354,16 @@ QString SessionTab::terminalThemeName() const
|
||||
return m_terminalThemeName;
|
||||
}
|
||||
|
||||
bool SessionTab::supportsThemeSelection() const
|
||||
{
|
||||
return m_useKodoTermForSsh || m_terminalOutput != nullptr;
|
||||
}
|
||||
|
||||
bool SessionTab::supportsClearAction() const
|
||||
{
|
||||
return m_useKodoTermForSsh || m_terminalOutput != nullptr;
|
||||
}
|
||||
|
||||
void SessionTab::onBackendStateChanged(SessionState state, const QString& message)
|
||||
{
|
||||
setState(state, message);
|
||||
@@ -322,6 +378,9 @@ void SessionTab::onBackendConnectionError(const QString& displayMessage, const Q
|
||||
{
|
||||
m_lastError = rawMessage.isEmpty() ? displayMessage : rawMessage;
|
||||
appendEvent(QStringLiteral("Error: %1").arg(displayMessage));
|
||||
if (!rawMessage.trimmed().isEmpty() && rawMessage.trimmed() != displayMessage.trimmed()) {
|
||||
appendEvent(QStringLiteral("Raw Error: %1").arg(rawMessage.trimmed()));
|
||||
}
|
||||
}
|
||||
|
||||
void SessionTab::onBackendOutputReceived(const QString& text)
|
||||
@@ -363,12 +422,21 @@ void SessionTab::setupUi()
|
||||
config.maxScrollback = 12000;
|
||||
m_sshTerminal->setConfig(config);
|
||||
rootLayout->addWidget(m_sshTerminal, 1);
|
||||
} else if (m_profile.protocol.compare(QStringLiteral("RDP"), Qt::CaseInsensitive) == 0) {
|
||||
m_rdpDisplay = new RdpDisplayWidget(this);
|
||||
rootLayout->addWidget(m_rdpDisplay, 1);
|
||||
} else {
|
||||
m_terminalOutput = new TerminalView(this);
|
||||
m_terminalOutput->setFont(defaultTerminalFont());
|
||||
m_terminalOutput->setMinimumHeight(260);
|
||||
m_terminalOutput->setPlaceholderText(
|
||||
QStringLiteral("Session is connecting. Type directly here once connected."));
|
||||
m_terminalOutput->setReadOnly(true);
|
||||
if (m_profile.protocol.compare(QStringLiteral("VNC"), Qt::CaseInsensitive) == 0) {
|
||||
m_terminalOutput->setPlaceholderText(
|
||||
QStringLiteral("Embedded VNC session output appears here when the backend is available."));
|
||||
} else {
|
||||
m_terminalOutput->setPlaceholderText(
|
||||
QStringLiteral("Session output appears here."));
|
||||
}
|
||||
rootLayout->addWidget(m_terminalOutput, 1);
|
||||
}
|
||||
|
||||
@@ -415,6 +483,33 @@ void SessionTab::setupUi()
|
||||
&TerminalView::terminalSizeChanged,
|
||||
this,
|
||||
[this](int columns, int rows) { emit requestTerminalSize(columns, rows); });
|
||||
} else if (m_rdpDisplay != nullptr) {
|
||||
connect(m_rdpDisplay,
|
||||
&RdpDisplayWidget::viewportSizeChanged,
|
||||
this,
|
||||
[this](int width, int height) { emit requestTerminalSize(width, height); });
|
||||
connect(m_rdpDisplay,
|
||||
&RdpDisplayWidget::keyInput,
|
||||
this,
|
||||
[this](int key, quint32 nativeScanCode, const QString& text, bool pressed, int modifiers) {
|
||||
emit requestKeyEvent(key, nativeScanCode, text, pressed, modifiers);
|
||||
});
|
||||
connect(m_rdpDisplay,
|
||||
&RdpDisplayWidget::mouseMoveInput,
|
||||
this,
|
||||
[this](int x, int y) { emit requestMouseMoveEvent(x, y); });
|
||||
connect(m_rdpDisplay,
|
||||
&RdpDisplayWidget::mouseButtonInput,
|
||||
this,
|
||||
[this](int x, int y, int button, bool pressed) {
|
||||
emit requestMouseButtonEvent(x, y, button, pressed);
|
||||
});
|
||||
connect(m_rdpDisplay,
|
||||
&RdpDisplayWidget::mouseWheelInput,
|
||||
this,
|
||||
[this](int x, int y, int deltaX, int deltaY) {
|
||||
emit requestMouseWheelEvent(x, y, deltaX, deltaY);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,7 +518,41 @@ std::optional<SessionConnectOptions> SessionTab::buildConnectOptions()
|
||||
SessionConnectOptions options;
|
||||
options.knownHostsPolicy = m_profile.knownHostsPolicy;
|
||||
|
||||
if (m_profile.protocol.compare(QStringLiteral("SSH"), Qt::CaseInsensitive) != 0) {
|
||||
const bool isSsh = m_profile.protocol.compare(QStringLiteral("SSH"), Qt::CaseInsensitive) == 0;
|
||||
const bool isRdp = m_profile.protocol.compare(QStringLiteral("RDP"), Qt::CaseInsensitive) == 0;
|
||||
|
||||
if (!isSsh && !isRdp) {
|
||||
return options;
|
||||
}
|
||||
|
||||
if (isRdp) {
|
||||
if (m_profile.authMode.compare(QStringLiteral("Password"), Qt::CaseInsensitive) != 0) {
|
||||
return options;
|
||||
}
|
||||
|
||||
bool accepted = false;
|
||||
const QString password = QInputDialog::getText(
|
||||
this,
|
||||
QStringLiteral("RDP Password"),
|
||||
QStringLiteral("Password for %1:")
|
||||
.arg(m_profile.username.trimmed().isEmpty()
|
||||
? m_profile.host
|
||||
: QStringLiteral("%1@%2").arg(m_profile.username, m_profile.host)),
|
||||
QLineEdit::Password,
|
||||
QString(),
|
||||
&accepted);
|
||||
if (!accepted) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (password.isEmpty()) {
|
||||
QMessageBox::warning(this,
|
||||
QStringLiteral("Connect"),
|
||||
QStringLiteral("Password is required for password authentication."));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
options.password = password;
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -481,28 +610,26 @@ std::optional<SessionConnectOptions> SessionTab::buildConnectOptions()
|
||||
|
||||
bool SessionTab::validateProfileForConnect()
|
||||
{
|
||||
if (m_profile.protocol.compare(QStringLiteral("SSH"), Qt::CaseInsensitive) != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_profile.host.trimmed().isEmpty()) {
|
||||
QMessageBox::warning(this,
|
||||
QStringLiteral("Connect"),
|
||||
QStringLiteral("SSH host is required."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_profile.username.trimmed().isEmpty()) {
|
||||
QMessageBox::warning(this,
|
||||
QStringLiteral("Connect"),
|
||||
QStringLiteral("SSH username is required."));
|
||||
QStringLiteral("%1 host is required.").arg(m_profile.protocol));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_profile.port < 1 || m_profile.port > 65535) {
|
||||
QMessageBox::warning(this,
|
||||
QStringLiteral("Connect"),
|
||||
QStringLiteral("SSH port must be between 1 and 65535."));
|
||||
QStringLiteral("Port must be between 1 and 65535."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((m_profile.protocol.compare(QStringLiteral("SSH"), Qt::CaseInsensitive) == 0
|
||||
|| m_profile.protocol.compare(QStringLiteral("RDP"), Qt::CaseInsensitive) == 0)
|
||||
&& m_profile.username.trimmed().isEmpty()) {
|
||||
QMessageBox::warning(this,
|
||||
QStringLiteral("Connect"),
|
||||
QStringLiteral("%1 username is required.").arg(m_profile.protocol));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -554,6 +681,14 @@ void SessionTab::refreshActionButtons()
|
||||
if (m_terminalOutput != nullptr) {
|
||||
m_terminalOutput->setEnabled(isConnected);
|
||||
m_terminalOutput->setFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_rdpDisplay != nullptr) {
|
||||
m_rdpDisplay->setEnabled(isConnected);
|
||||
if (isConnected) {
|
||||
m_rdpDisplay->setFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user