Commit 8c7457b8 authored by Alexander Volkov's avatar Alexander Volkov Committed by Alexander Volkov
Browse files

Add highdpi support for RDP and VNC

Summary:
In highdpi mode coordinates are specified in device independent pixels,
and Qt auto-scales everything by devicePixelRatio, i.e. the ratio between
the device independent and device pixel coordinate systems.

Thus for RDP we should pass the size in device pixels to xfreerdp.

For VNC we should avoid auto-scaling, which can be done by setting
devicePixelRatio of the paint device for the painted image, and
the size of the image should be converted to device independent
pixels where it's needed.

Reviewers: #kde_applications, davidedmundson, uwolfer, ahmadsamir, murrant, cfeck, ngraham

Reviewed By: murrant

Subscribers: yuyichao, ngraham, alexeymin, aacid

Differential Revision: https://phabricator.kde.org/D20790
parent 34080176
......@@ -26,6 +26,10 @@
#include "settings.h"
#include <QScreen>
#include <QWindow>
#include <QGuiApplication>
static const QStringList keymaps = (QStringList()
<< QStringLiteral("ar")
<< QStringLiteral("cs")
......@@ -147,10 +151,12 @@ void RdpHostPreferences::updateWidthHeight(int index)
rdpUi.kcfg_Width->setValue(1600);
break;
case 5: {
QDesktopWidget *desktop = QApplication::desktop();
int currentScreen = desktop->screenNumber(rdpUi.kcfg_Height);
rdpUi.kcfg_Height->setValue(desktop->screenGeometry(currentScreen).height());
rdpUi.kcfg_Width->setValue(desktop->screenGeometry(currentScreen).width());
QWindow *window = rdpUi.kcfg_Width->window()->windowHandle();
QScreen *screen = window ? window->screen() : qGuiApp->primaryScreen();
const QSize size = screen->size() * screen->devicePixelRatio();
rdpUi.kcfg_Width->setValue(size.width());
rdpUi.kcfg_Height->setValue(size.height());
break;
}
case 7:
......
......@@ -28,7 +28,6 @@
#include "hostpreferences.h"
#include "ui_rdppreferences.h"
#include <QDesktopWidget>
#include <QFileDialog>
class RdpHostPreferences : public HostPreferences
......
......@@ -169,21 +169,19 @@ bool RdpView::start()
QStringList arguments;
int width, height;
QSize size;
const auto dpr = devicePixelRatioF();
if (m_hostPreferences->width() > 0) {
width = m_hostPreferences->width();
height = m_hostPreferences->height();
size = QSize(m_hostPreferences->width(), m_hostPreferences->height());
} else {
width = this->parentWidget()->size().width();
height = this->parentWidget()->size().height();
size = this->parentWidget()->size() * dpr;
}
m_containerWidget->setFixedWidth(width);
m_containerWidget->setFixedHeight(height);
setMaximumSize(width, height);
m_containerWidget->setFixedSize(size / dpr);
setMaximumSize(size / dpr);
if (versionOutput.contains(QLatin1String(" 1.0"))) {
qCDebug(KRDC) << "Use FreeRDP 1.0 compatible arguments";
arguments << QStringLiteral("-g") << QString::number(width) + QLatin1Char('x') + QString::number(height);
arguments << QStringLiteral("-g") << QString::number(size.width()) + QLatin1Char('x') + QString::number(size.height());
arguments << QStringLiteral("-k") << keymapToXfreerdp(m_hostPreferences->keyboardLayout());
......@@ -276,8 +274,8 @@ bool RdpView::start()
qCDebug(KRDC) << "Use FreeRDP 1.1+ compatible arguments";
arguments << QStringLiteral("-decorations");
arguments << QStringLiteral("/w:") + QString::number(width);
arguments << QStringLiteral("/h:") + QString::number(height);
arguments << QStringLiteral("/w:") + QString::number(size.width());
arguments << QStringLiteral("/h:") + QString::number(size.height());
arguments << QStringLiteral("/kbd:") + keymapToXfreerdp(m_hostPreferences->keyboardLayout());
......
......@@ -245,6 +245,7 @@ void VncClientThread::updatefb(int x, int y, int w, int h)
return; // sending data to a stopped thread is not a good idea
}
img.setDevicePixelRatio(m_devicePixelRatio);
setImage(img);
emitUpdated(x, y, w, h);
......@@ -359,6 +360,7 @@ VncClientThread::VncClientThread(QObject *parent)
: QThread(parent)
, frameBuffer(nullptr)
, cl(nullptr)
, m_devicePixelRatio(1.0)
, m_stopped(false)
{
// We choose a small value for interval...after all if the connection is
......@@ -450,6 +452,11 @@ void VncClientThread::setQuality(RemoteView::Quality quality)
}
}
void VncClientThread::setDevicePixelRatio(qreal dpr)
{
m_devicePixelRatio = dpr;
}
void VncClientThread::setColorDepth(ColorDepth colorDepth)
{
m_colorDepth = colorDepth;
......
......@@ -116,6 +116,7 @@ public:
void setHost(const QString &host);
void setPort(int port);
void setQuality(RemoteView::Quality quality);
void setDevicePixelRatio(qreal dpr);
void setPassword(const QString &password) {
m_password = password;
}
......@@ -189,6 +190,7 @@ private:
bool m_showLocalCursor;
QMutex mutex;
RemoteView::Quality m_quality;
qreal m_devicePixelRatio;
ColorDepth m_colorDepth;
QQueue<ClientEvent* > m_eventQueue;
//color table for 8bit indexed colors
......
......@@ -25,7 +25,9 @@
#include "settings.h"
#include <QDesktopWidget>
#include <QScreen>
#include <QWindow>
#include <QGuiApplication>
static const char quality_config_key[] = "quality";
static const char use_ssh_tunnel_config_key[] = "use_ssh_tunnel";
......@@ -101,10 +103,12 @@ void VncHostPreferences::updateScalingWidthHeight(int index)
vncUi.kcfg_ScalingWidth->setValue(1600);
break;
case 5: {
QDesktopWidget *desktop = QApplication::desktop();
int currentScreen = desktop->screenNumber(vncUi.kcfg_ScalingHeight);
vncUi.kcfg_ScalingHeight->setValue(desktop->screenGeometry(currentScreen).height());
vncUi.kcfg_ScalingWidth->setValue(desktop->screenGeometry(currentScreen).width());
QWindow *window = vncUi.kcfg_ScalingWidth->window()->windowHandle();
QScreen *screen = window ? window->screen() : qGuiApp->primaryScreen();
const QSize size = screen->size() * screen->devicePixelRatio();
vncUi.kcfg_ScalingWidth->setValue(size.width());
vncUi.kcfg_ScalingHeight->setValue(size.height());
break;
}
case 6:
......
......@@ -116,7 +116,7 @@ bool VncView::eventFilter(QObject *obj, QEvent *event)
QSize VncView::framebufferSize()
{
return m_frame.size();
return m_frame.size() / devicePixelRatioF();
}
QSize VncView::sizeHint() const
......@@ -135,8 +135,9 @@ void VncView::scaleResize(int w, int h)
qCDebug(KRDC) << w << h;
if (m_scale) {
m_verticalFactor = (qreal) h / m_frame.height();
m_horizontalFactor = (qreal) w / m_frame.width();
const QSize frameSize = m_frame.size() / m_frame.devicePixelRatio();
m_verticalFactor = (qreal) h / frameSize.height();
m_horizontalFactor = (qreal) w / frameSize.width();
#ifndef QTONLY
if (Settings::keepAspectRatio()) {
......@@ -146,8 +147,8 @@ void VncView::scaleResize(int w, int h)
m_verticalFactor = m_horizontalFactor = qMin(m_verticalFactor, m_horizontalFactor);
#endif
const qreal newW = m_frame.width() * m_horizontalFactor;
const qreal newH = m_frame.height() * m_verticalFactor;
const qreal newW = frameSize.width() * m_horizontalFactor;
const qreal newH = frameSize.height() * m_verticalFactor;
setMaximumSize(newW, newH); //This is a hack to force Qt to center the view in the scroll area
resize(newW, newH);
}
......@@ -258,6 +259,7 @@ bool VncView::start()
#endif
vncThread.setQuality(quality);
vncThread.setDevicePixelRatio(devicePixelRatioF());
// set local cursor on by default because low quality mostly means slow internet connection
if (quality == RemoteView::Low) {
......@@ -453,8 +455,9 @@ void VncView::updateImage(int x, int y, int w, int h)
if (m_scale) {
#ifndef QTONLY
qCDebug(KRDC) << "Setting initial size w:" <<m_hostPreferences->width() << " h:" << m_hostPreferences->height();
emit framebufferSizeChanged(m_hostPreferences->width(), m_hostPreferences->height());
scaleResize(m_hostPreferences->width(), m_hostPreferences->height());
QSize frameSize = QSize(m_hostPreferences->width(), m_hostPreferences->height()) / devicePixelRatioF();
emit framebufferSizeChanged(frameSize.width(), frameSize.height());
scaleResize(frameSize.width(), frameSize.height());
qCDebug(KRDC) << "m_frame.size():" << m_frame.size() << "size()" << size();
#else
//TODO: qtonly alternative
......@@ -475,7 +478,8 @@ void VncView::updateImage(int x, int y, int w, int h)
#endif
}
if ((y == 0 && x == 0) && (m_frame.size() != size())) {
const QSize frameSize = m_frame.size() / m_frame.devicePixelRatio();
if ((y == 0 && x == 0) && (frameSize != size())) {
qCDebug(KRDC) << "Updating framebuffer size";
if (m_scale) {
setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
......@@ -483,14 +487,15 @@ void VncView::updateImage(int x, int y, int w, int h)
scaleResize(parentWidget()->width(), parentWidget()->height());
} else {
qCDebug(KRDC) << "Resizing: " << m_frame.width() << m_frame.height();
resize(m_frame.width(), m_frame.height());
setMaximumSize(m_frame.width(), m_frame.height()); //This is a hack to force Qt to center the view in the scroll area
setMinimumSize(m_frame.width(), m_frame.height());
emit framebufferSizeChanged(m_frame.width(), m_frame.height());
resize(frameSize);
setMaximumSize(frameSize); //This is a hack to force Qt to center the view in the scroll area
setMinimumSize(frameSize);
emit framebufferSizeChanged(frameSize.width(), frameSize.height());
}
}
repaint(QRectF(x * m_horizontalFactor, y * m_verticalFactor, w * m_horizontalFactor, h * m_verticalFactor).toAlignedRect());
const auto dpr = m_frame.devicePixelRatio();
repaint(QRectF(x / dpr * m_horizontalFactor, y / dpr * m_verticalFactor, w / dpr * m_horizontalFactor, h / dpr * m_verticalFactor).toAlignedRect());
}
void VncView::setViewOnly(bool viewOnly)
......@@ -533,9 +538,10 @@ void VncView::enableScaling(bool scale)
m_verticalFactor = 1.0;
m_horizontalFactor = 1.0;
setMaximumSize(m_frame.width(), m_frame.height()); //This is a hack to force Qt to center the view in the scroll area
setMinimumSize(m_frame.width(), m_frame.height());
resize(m_frame.width(), m_frame.height());
const QSize frameSize = m_frame.size() / m_frame.devicePixelRatio();
setMaximumSize(frameSize); //This is a hack to force Qt to center the view in the scroll area
setMinimumSize(frameSize);
resize(frameSize);
}
}
......@@ -560,9 +566,10 @@ void VncView::paintEvent(QPaintEvent *event)
QPainter painter(this);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
const auto dpr = m_frame.devicePixelRatio();
const QRectF dstRect = event->rect();
const QRectF srcRect(dstRect.x() / m_horizontalFactor, dstRect.y() / m_verticalFactor,
dstRect.width() / m_horizontalFactor, dstRect.height() / m_verticalFactor);
const QRectF srcRect(dstRect.x() * dpr / m_horizontalFactor, dstRect.y() * dpr / m_verticalFactor,
dstRect.width() * dpr / m_horizontalFactor, dstRect.height() * dpr / m_verticalFactor);
painter.drawImage(dstRect, m_frame, srcRect);
RemoteView::paintEvent(event);
......@@ -622,7 +629,12 @@ void VncView::mouseEventHandler(QMouseEvent *e)
}
}
vncThread.mouseEvent(qRound(e->x() / m_horizontalFactor), qRound(e->y() / m_verticalFactor), m_buttonMask);
const auto dpr = devicePixelRatioF();
QPointF screenPos = e->screenPos();
// We need to restore mouse position in device coordinates.
// QMouseEvent::localPos() can be rounded (bug in Qt), but QMouseEvent::screenPos() is not.
QPointF pos = (e->pos() + (screenPos - screenPos.toPoint())) * dpr;
vncThread.mouseEvent(qRound(pos.x() / m_horizontalFactor), qRound(pos.y() / m_verticalFactor), m_buttonMask);
}
void VncView::wheelEventHandler(QWheelEvent *event)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment