Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions
+207
View File
@@ -0,0 +1,207 @@
#include "rdp_display_widget.h"
#include <QKeyEvent>
#include <QMouseEvent>
#include <QPainter>
#include <QResizeEvent>
#include <QTimer>
#include <QWheelEvent>
#include <QtGlobal>
namespace {
QSize sanitizeSize(const QSize& size)
{
return QSize(qMax(1, size.width()), qMax(1, size.height()));
}
}
RdpDisplayWidget::RdpDisplayWidget(QWidget* parent)
: QWidget(parent), m_remoteSize(1280, 720)
{
setFocusPolicy(Qt::StrongFocus);
setMouseTracking(true);
setAutoFillBackground(false);
setMinimumSize(320, 200);
QTimer::singleShot(0, this, [this]() {
const QSize size = sanitizeSize(this->size());
emit viewportSizeChanged(size.width(), size.height());
});
}
void RdpDisplayWidget::setFrame(const QImage& frame)
{
if (frame.isNull()) {
return;
}
m_frame = frame;
m_remoteSize = sanitizeSize(frame.size());
update();
}
void RdpDisplayWidget::setRemoteDesktopSize(int width, int height)
{
if (width < 1 || height < 1) {
return;
}
const QSize nextSize(width, height);
if (m_remoteSize == nextSize) {
return;
}
m_remoteSize = nextSize;
update();
}
void RdpDisplayWidget::clearFrame()
{
m_frame = QImage();
update();
}
void RdpDisplayWidget::paintEvent(QPaintEvent* event)
{
Q_UNUSED(event);
QPainter painter(this);
painter.fillRect(rect(), QColor(QStringLiteral("#101214")));
const QRectF target = renderRect();
if (!m_frame.isNull()) {
painter.drawImage(target, m_frame);
} else {
painter.setPen(QColor(QStringLiteral("#b0bec5")));
painter.drawText(rect(),
Qt::AlignCenter,
QStringLiteral("Waiting for remote desktop frame..."));
}
}
void RdpDisplayWidget::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
const QSize size = sanitizeSize(event->size());
emit viewportSizeChanged(size.width(), size.height());
}
void RdpDisplayWidget::keyPressEvent(QKeyEvent* event)
{
if (event == nullptr || event->isAutoRepeat()) {
return;
}
emit keyInput(event->key(),
event->nativeScanCode(),
event->text(),
true,
static_cast<int>(event->modifiers()));
event->accept();
}
void RdpDisplayWidget::keyReleaseEvent(QKeyEvent* event)
{
if (event == nullptr || event->isAutoRepeat()) {
return;
}
emit keyInput(event->key(),
event->nativeScanCode(),
event->text(),
false,
static_cast<int>(event->modifiers()));
event->accept();
}
void RdpDisplayWidget::mousePressEvent(QMouseEvent* event)
{
if (event == nullptr) {
return;
}
setFocus(Qt::MouseFocusReason);
const QPoint mapped = mapToRemote(event->position());
emit mouseButtonInput(mapped.x(), mapped.y(), static_cast<int>(event->button()), true);
event->accept();
}
void RdpDisplayWidget::mouseReleaseEvent(QMouseEvent* event)
{
if (event == nullptr) {
return;
}
const QPoint mapped = mapToRemote(event->position());
emit mouseButtonInput(mapped.x(), mapped.y(), static_cast<int>(event->button()), false);
event->accept();
}
void RdpDisplayWidget::mouseMoveEvent(QMouseEvent* event)
{
if (event == nullptr) {
return;
}
const QPoint mapped = mapToRemote(event->position());
emit mouseMoveInput(mapped.x(), mapped.y());
event->accept();
}
void RdpDisplayWidget::wheelEvent(QWheelEvent* event)
{
if (event == nullptr) {
return;
}
const QPoint mapped = mapToRemote(event->position());
const QPoint angle = event->angleDelta();
emit mouseWheelInput(mapped.x(), mapped.y(), angle.x(), angle.y());
event->accept();
}
QRectF RdpDisplayWidget::renderRect() const
{
const QSize remote = effectiveRemoteSize();
const QRectF area = rect();
if (area.isEmpty()) {
return QRectF();
}
const qreal scale = qMin(area.width() / remote.width(), area.height() / remote.height());
const qreal drawWidth = remote.width() * scale;
const qreal drawHeight = remote.height() * scale;
const qreal x = area.x() + ((area.width() - drawWidth) * 0.5);
const qreal y = area.y() + ((area.height() - drawHeight) * 0.5);
return QRectF(x, y, drawWidth, drawHeight);
}
QPoint RdpDisplayWidget::mapToRemote(const QPointF& pos) const
{
const QSize remote = effectiveRemoteSize();
const QRectF target = renderRect();
if (target.isEmpty()) {
return QPoint(0, 0);
}
const qreal clampedX = qBound(target.left(), pos.x(), target.right());
const qreal clampedY = qBound(target.top(), pos.y(), target.bottom());
const qreal normalizedX = (clampedX - target.left()) / qMax(1.0, target.width());
const qreal normalizedY = (clampedY - target.top()) / qMax(1.0, target.height());
const int remoteX = qBound(0, static_cast<int>(normalizedX * remote.width()), remote.width() - 1);
const int remoteY = qBound(0, static_cast<int>(normalizedY * remote.height()), remote.height() - 1);
return QPoint(remoteX, remoteY);
}
QSize RdpDisplayWidget::effectiveRemoteSize() const
{
if (m_remoteSize.width() > 0 && m_remoteSize.height() > 0) {
return m_remoteSize;
}
if (!m_frame.isNull()) {
return sanitizeSize(m_frame.size());
}
return QSize(1280, 720);
}