Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
@@ -0,0 +1,398 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#include "pws_vnc_viewer_dialog.h"
|
||||
#include "ui_pws_vnc_viewer_dialog.h"
|
||||
|
||||
#include <LtsProjectionWorkstation/logger.h>
|
||||
|
||||
#include <QMutexLocker>
|
||||
#include <QPainter>
|
||||
#include <QOpenGLPaintDevice>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
||||
namespace ProjectionWorkstation
|
||||
{
|
||||
|
||||
// Dialog
|
||||
|
||||
VncViewerDialog::VncViewerDialog(const QString &name, QWidget *parent)
|
||||
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::SeamlessDialog, false)
|
||||
, m_ui(new Ui::VncViewerDialog)
|
||||
, m_handler(nullptr)
|
||||
, m_renderArea(nullptr)
|
||||
, m_mouseButtonMask(0)
|
||||
, m_mouseScrollReleaseTimer(new QTimer(this))
|
||||
, m_name(name)
|
||||
, m_isConnected(false)
|
||||
, m_logger(LtsProjectionWorkstation::getLogger(QStringLiteral("VncViewerDialog")))
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
}
|
||||
|
||||
VncViewerDialog::~VncViewerDialog()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
bool VncViewerDialog::init(const QHostAddress &host, quint16 port, const QSize &remoteResolution)
|
||||
{
|
||||
m_handler = new VncHandler(host, port, this);
|
||||
m_renderArea = new VncRenderArea(remoteResolution, this);
|
||||
m_remoteResolution = remoteResolution;
|
||||
m_ui->gridLayout->addWidget(m_renderArea);
|
||||
|
||||
connect(m_handler, &VncHandler::frameBufferUpdateReceived, m_renderArea, &VncRenderArea::frameBufferUpdateReceived);
|
||||
connect(m_handler, &VncHandler::cutTextReceived, this, &VncViewerDialog::handleServerCuttext);
|
||||
connect(m_handler, &VncHandler::beepReceived, this, &QApplication::beep);
|
||||
connect(m_handler, &VncHandler::connected, this, &VncViewerDialog::show);
|
||||
connect(m_handler, &VncHandler::disconnected, this, &VncViewerDialog::slotOnDisconnected);
|
||||
connect(m_mouseScrollReleaseTimer, &QTimer::timeout, this, &VncViewerDialog::onScrollRelease);
|
||||
setMouseTracking(true);
|
||||
|
||||
m_mouseScrollReleaseTimer->setSingleShot(true);
|
||||
m_mouseScrollReleaseTimer->setInterval(5);
|
||||
|
||||
m_handler->connectToHost();
|
||||
|
||||
hide();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VncViewerDialog::retranslate()
|
||||
{
|
||||
translate(m_ui, m_name);
|
||||
}
|
||||
|
||||
void VncViewerDialog::handleServerCuttext(const QString &cutText)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Got cuttext: " << cutText;
|
||||
}
|
||||
|
||||
void VncViewerDialog::slotOnDisconnected()
|
||||
{
|
||||
// closeRequest();
|
||||
// m_renderArea->clear();
|
||||
emit disconnected();
|
||||
}
|
||||
|
||||
void VncViewerDialog::focusInEvent(QFocusEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
grabKeyboard();
|
||||
// setCursor(Qt::BlankCursor);
|
||||
if (m_renderArea)
|
||||
{
|
||||
QByteArray data = RfbProtocol::CTS::RfbFrameBufferUpdateRequest(false, 0, 0, m_renderArea->frameSize().width(), m_renderArea->frameSize().height());
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
void VncViewerDialog::focusOutEvent(QFocusEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
releaseKeyboard();
|
||||
// unsetCursor();
|
||||
}
|
||||
|
||||
void VncViewerDialog::onScrollRelease()
|
||||
{
|
||||
m_mouseButtonMask &= ~(3 << 3);
|
||||
|
||||
QByteArray data = RfbProtocol::CTS::RfbPointerEvent(m_mouseButtonMask, m_lastScrollPos.x(), m_lastScrollPos.y());
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
|
||||
void VncViewerDialog::onSlotConnected()
|
||||
{
|
||||
m_isConnected = true;
|
||||
}
|
||||
|
||||
void VncViewerDialog::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
if (event->button() == Qt::LeftButton)
|
||||
m_mouseButtonMask |= (1 << 0);
|
||||
else if (event->button() == Qt::MidButton)
|
||||
m_mouseButtonMask |= (1 << 1);
|
||||
else if (event->button() == Qt::RightButton)
|
||||
m_mouseButtonMask |= (1 << 2);
|
||||
|
||||
QPoint mapped = mapMousePosition(event->pos());
|
||||
|
||||
QByteArray data = RfbProtocol::CTS::RfbPointerEvent(m_mouseButtonMask, mapped.x(), mapped.y());
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
|
||||
void VncViewerDialog::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
if (event->button() == Qt::LeftButton)
|
||||
m_mouseButtonMask &= ~(1 << 0);
|
||||
else if (event->button() == Qt::MidButton)
|
||||
m_mouseButtonMask &= ~(1 << 1);
|
||||
else if (event->button() == Qt::RightButton)
|
||||
m_mouseButtonMask &= ~(1 << 2);
|
||||
|
||||
QPoint mapped = mapMousePosition(event->pos());
|
||||
|
||||
QByteArray data = RfbProtocol::CTS::RfbPointerEvent(m_mouseButtonMask, mapped.x(), mapped.y());
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
|
||||
void VncViewerDialog::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
QPoint mapped = mapMousePosition(event->pos());
|
||||
QByteArray data = RfbProtocol::CTS::RfbPointerEvent(m_mouseButtonMask, mapped.x(), mapped.y());
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
|
||||
void VncViewerDialog::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
QByteArray data = RfbProtocol::CTS::RfbKeyEvent(event->nativeVirtualKey(), true);
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
|
||||
void VncViewerDialog::keyReleaseEvent(QKeyEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
QByteArray data = RfbProtocol::CTS::RfbKeyEvent(event->nativeVirtualKey(), false);
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
|
||||
void VncViewerDialog::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
if (!m_mouseScrollReleaseTimer->isActive())
|
||||
{
|
||||
if (event->angleDelta().y() > 0)
|
||||
m_mouseButtonMask |= (1 << 3);
|
||||
else if (event->angleDelta().y() < 0)
|
||||
m_mouseButtonMask |= (1 << 4);
|
||||
|
||||
QPoint mapped = mapMousePosition(event->pos());
|
||||
|
||||
QByteArray data = RfbProtocol::CTS::RfbPointerEvent(m_mouseButtonMask, mapped.x(), mapped.y());
|
||||
m_handler->writeData(data);
|
||||
m_lastScrollPos = mapped;
|
||||
m_mouseScrollReleaseTimer->start();
|
||||
}
|
||||
}
|
||||
|
||||
void VncViewerDialog::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
if (m_isConnected && m_renderArea)
|
||||
{
|
||||
emit m_renderArea->resized();
|
||||
QByteArray data = RfbProtocol::CTS::RfbFrameBufferUpdateRequest(false, 0, 0, m_renderArea->frameSize().width(), m_renderArea->frameSize().height());
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
void VncViewerDialog::showEvent(QShowEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
if (m_isConnected && m_renderArea)
|
||||
{
|
||||
QByteArray data = RfbProtocol::CTS::RfbFrameBufferUpdateRequest(false, 0, 0, m_renderArea->frameSize().width(), m_renderArea->frameSize().height());
|
||||
m_handler->writeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int mapValue(double val, double maxIn, double maxOut)
|
||||
{
|
||||
val = qBound(0., val, maxIn);
|
||||
return qRound(val * (maxOut / maxIn));
|
||||
}
|
||||
|
||||
QPoint VncViewerDialog::mapMousePosition(const QPoint &point) const
|
||||
{
|
||||
QPoint clienPoint = m_renderArea->mapFromParent(point);
|
||||
int x = mapValue(clienPoint.x(), m_renderArea->scaledSize().width(), m_renderArea->frameSize().width());
|
||||
int y = mapValue(clienPoint.y(), m_renderArea->scaledSize().height(), m_renderArea->frameSize().height());
|
||||
return QPoint(x, y);
|
||||
}
|
||||
|
||||
|
||||
// Render Area Worker
|
||||
VncRenderAreaWorker::VncRenderAreaWorker()
|
||||
: m_logger(LtsProjectionWorkstation::getLogger(QStringLiteral("VncRenderArea")))
|
||||
{
|
||||
qRegisterMetaType<QVector<RfbProtocol::RfbRectangle>>("QVector<RfbProtocol::RfbRectangle>");
|
||||
}
|
||||
|
||||
void VncRenderAreaWorker::onRfbFrameBufferUpdate(const QVector<RfbProtocol::RfbRectangle> &rectangles)
|
||||
{
|
||||
if (rectangles.isEmpty())
|
||||
return;
|
||||
|
||||
QPainter painter(&m_renderedImage);
|
||||
// no dequeue here otherwise rectangles() is dangling
|
||||
for (const RfbProtocol::RfbRectangle &rect : rectangles)
|
||||
{
|
||||
if (rect.pixelData.isEmpty())
|
||||
{
|
||||
WARNING_LOG(m_logger) << "No valid vnc pixel data... skipping frame";
|
||||
continue;
|
||||
}
|
||||
QImage img(reinterpret_cast<const unsigned char *>(rect.pixelData.data()),
|
||||
rect.width,
|
||||
rect.height,
|
||||
QImage::Format_RGB32);
|
||||
if (img.isNull())
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Could not decode image data... skipping vnc frame";
|
||||
continue;
|
||||
}
|
||||
painter.drawImage(QPoint(rect.xpos, rect.ypos), img);
|
||||
}
|
||||
painter.end();
|
||||
|
||||
QImage img = m_renderedImage.scaled(m_scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
if (img.size() != m_scaledSizeAspectRatio)
|
||||
{
|
||||
m_scaledSizeAspectRatio = img.size();
|
||||
emit newScaleSizeAspectRatio(m_scaledSizeAspectRatio);
|
||||
}
|
||||
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
std::swap(img, m_scaledImage);
|
||||
}
|
||||
|
||||
emit newImageAvailable();
|
||||
}
|
||||
|
||||
void VncRenderAreaWorker::setImageSize(QSize s)
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
m_renderedImage = QImage(s, QImage::Format_RGB32);
|
||||
}
|
||||
|
||||
void VncRenderAreaWorker::setScaledSize(QSize s)
|
||||
{
|
||||
if (m_scaledSize == s)
|
||||
return;
|
||||
|
||||
QMutexLocker l(&m_mutex);
|
||||
m_scaledSize = s;
|
||||
if (!m_renderedImage.isNull())
|
||||
{
|
||||
m_scaledImage = m_renderedImage.scaled(m_scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
m_scaledSizeAspectRatio = m_scaledImage.size();
|
||||
emit newScaleSizeAspectRatio(m_scaledSizeAspectRatio);
|
||||
}
|
||||
}
|
||||
|
||||
QImage VncRenderAreaWorker::renderedImage() const
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
return m_scaledImage;
|
||||
}
|
||||
|
||||
|
||||
//Render Area
|
||||
|
||||
VncRenderArea::VncRenderArea(const QSize remoteResolution, QWidget *parent)
|
||||
: QOpenGLWidget(parent)
|
||||
, m_imageRenderThread(new QThread(this))
|
||||
, m_renderAreaWorker(new VncRenderAreaWorker)
|
||||
, m_frameSize(remoteResolution)
|
||||
, m_fbo(nullptr)
|
||||
, m_logger(LtsProjectionWorkstation::getLogger(QStringLiteral("VncRenderArea")))
|
||||
{
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
connect(this, &VncRenderArea::frameBufferUpdateReceived,
|
||||
m_renderAreaWorker, &VncRenderAreaWorker::onRfbFrameBufferUpdate);
|
||||
connect(m_renderAreaWorker, &VncRenderAreaWorker::newImageAvailable,
|
||||
this, &VncRenderArea::prepareRender);
|
||||
connect(m_renderAreaWorker, &VncRenderAreaWorker::newScaleSizeAspectRatio,
|
||||
this, [this](QSize s) { m_scaledSizeAspectRatio = s; });
|
||||
m_renderAreaWorker->setImageSize(remoteResolution);
|
||||
m_renderAreaWorker->moveToThread(m_imageRenderThread);
|
||||
m_imageRenderThread->start();
|
||||
}
|
||||
|
||||
VncRenderArea::~VncRenderArea()
|
||||
{
|
||||
delete m_fbo;
|
||||
|
||||
m_imageRenderThread->quit();
|
||||
m_imageRenderThread->wait(2000);
|
||||
delete m_imageRenderThread;
|
||||
delete m_renderAreaWorker;
|
||||
}
|
||||
|
||||
void VncRenderArea::clear()
|
||||
{
|
||||
if (context())
|
||||
{
|
||||
glClearColor(1, 1, 1, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void VncRenderArea::initializeGL()
|
||||
{
|
||||
initializeOpenGLFunctions();
|
||||
m_blitter.create();
|
||||
}
|
||||
|
||||
void VncRenderArea::paintGL()
|
||||
{
|
||||
if (context() && m_fbo)
|
||||
{
|
||||
m_blitter.bind();
|
||||
const QRect targetRect(QPoint(0, 0), m_fbo->size());
|
||||
const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(targetRect, targetRect); // no scaling here, is not anti-aliased
|
||||
m_blitter.blit(m_fbo->texture(), target, QOpenGLTextureBlitter::OriginBottomLeft);
|
||||
m_blitter.release();
|
||||
}
|
||||
}
|
||||
|
||||
void VncRenderArea::maybeResized()
|
||||
{
|
||||
rebuildFbo(size());
|
||||
m_renderAreaWorker->setScaledSize(size());
|
||||
}
|
||||
|
||||
void VncRenderArea::prepareRender()
|
||||
{
|
||||
if (context())
|
||||
{
|
||||
if (!m_fbo || m_fbo->size() != size())
|
||||
maybeResized();
|
||||
m_fbo->bind();
|
||||
QOpenGLPaintDevice device(size());
|
||||
QPainter painter(&device);
|
||||
painter.drawImage(QPoint(0, 0), m_renderAreaWorker->renderedImage());
|
||||
painter.end();
|
||||
m_fbo->release();
|
||||
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void VncRenderArea::rebuildFbo(QSize newSize)
|
||||
{
|
||||
delete m_fbo;
|
||||
QOpenGLFramebufferObjectFormat format;
|
||||
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
|
||||
m_fbo = new QOpenGLFramebufferObject(newSize, format);
|
||||
}
|
||||
|
||||
} // ProjectionWorkstation
|
||||
Reference in new issue
Block a user