Add high DPI support to internal KWin windows
Summary: So far we didn't try to do high DPI on kwin internal windows, such as the user context menu and tab bars and whatever. Due to wayland scaling they were the correct phyiscal size but upscaled. This patch fixes our QPA to enable Qt's high-dpi support. BUG: 402853 Note icons are still low res. This is because the global QGuiApplication::devicePixelRatio which is the max of all connected screens is static for the duration of the app. QIcon uses this when determining the DPR to use. This will require a Qt change. Test Plan: Ran at 2x on my normal DPI screen (as that's easier to see anything) * User action menu is high DPI * Window deco tooltips are still fine * Tab switcher is high DPI * Overlay in present windows Desktop grid are still ok Reviewers: #kwin Subscribers: zzag, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D18042
This commit is contained in:
parent
b7f6e57eff
commit
2df9d22a08
8 changed files with 69 additions and 13 deletions
|
@ -33,6 +33,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include <KWayland/Client/seat.h>
|
#include <KWayland/Client/seat.h>
|
||||||
#include <KWayland/Client/shell.h>
|
#include <KWayland/Client/shell.h>
|
||||||
|
|
||||||
|
#include <KWayland/Server/surface_interface.h>
|
||||||
|
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
|
|
||||||
using namespace KWayland::Client;
|
using namespace KWayland::Client;
|
||||||
|
@ -64,6 +66,7 @@ private Q_SLOTS:
|
||||||
void testModifierClickUnrestrictedMove();
|
void testModifierClickUnrestrictedMove();
|
||||||
void testModifierScroll();
|
void testModifierScroll();
|
||||||
void testPopup();
|
void testPopup();
|
||||||
|
void testScale();
|
||||||
};
|
};
|
||||||
|
|
||||||
class HelperWindow : public QRasterWindow
|
class HelperWindow : public QRasterWindow
|
||||||
|
@ -687,6 +690,29 @@ void InternalWindowTest::testPopup()
|
||||||
QCOMPARE(internalClient->isPopupWindow(), true);
|
QCOMPARE(internalClient->isPopupWindow(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InternalWindowTest::testScale()
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection,
|
||||||
|
Q_ARG(int, 2),
|
||||||
|
Q_ARG(QVector<QRect>, QVector<QRect>({QRect(0,0,1280, 1024), QRect(1280/2, 0, 1280, 1024)})),
|
||||||
|
Q_ARG(QVector<int>, QVector<int>({2,2})));
|
||||||
|
|
||||||
|
QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded);
|
||||||
|
QVERIFY(clientAddedSpy.isValid());
|
||||||
|
HelperWindow win;
|
||||||
|
win.setGeometry(0, 0, 100, 100);
|
||||||
|
win.setFlags(win.flags() | Qt::Popup);
|
||||||
|
win.show();
|
||||||
|
QCOMPARE(win.devicePixelRatio(), 2.0);
|
||||||
|
QVERIFY(clientAddedSpy.wait());
|
||||||
|
QCOMPARE(clientAddedSpy.count(), 1);
|
||||||
|
auto internalClient = clientAddedSpy.first().first().value<ShellClient*>();
|
||||||
|
QCOMPARE(internalClient->surface()->scale(), 2);
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(int, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WAYLANDTEST_MAIN(KWin::InternalWindowTest)
|
WAYLANDTEST_MAIN(KWin::InternalWindowTest)
|
||||||
|
|
|
@ -207,9 +207,9 @@ int main(int argc, char *argv[]) \
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef NO_XWAYLAND
|
#ifdef NO_XWAYLAND
|
||||||
#define WAYLANDTEST_MAIN(TestObject) WAYLANDTEST_MAIN_HELPER(TestObject, QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling), KWin::Application::OperationModeWaylandOnly)
|
#define WAYLANDTEST_MAIN(TestObject) WAYLANDTEST_MAIN_HELPER(TestObject, QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps), KWin::Application::OperationModeWaylandOnly)
|
||||||
#else
|
#else
|
||||||
#define WAYLANDTEST_MAIN(TestObject) WAYLANDTEST_MAIN_HELPER(TestObject, QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling), KWin::Application::OperationModeXwayland)
|
#define WAYLANDTEST_MAIN(TestObject) WAYLANDTEST_MAIN_HELPER(TestObject, QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps), KWin::Application::OperationModeXwayland)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -553,7 +553,7 @@ int main(int argc, char * argv[])
|
||||||
qunsetenv("QT_DEVICE_PIXEL_RATIO");
|
qunsetenv("QT_DEVICE_PIXEL_RATIO");
|
||||||
qputenv("QT_IM_MODULE", "qtvirtualkeyboard");
|
qputenv("QT_IM_MODULE", "qtvirtualkeyboard");
|
||||||
qputenv("QSG_RENDER_LOOP", "basic");
|
qputenv("QSG_RENDER_LOOP", "basic");
|
||||||
QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
|
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||||
KWin::ApplicationWayland a(argc, argv);
|
KWin::ApplicationWayland a(argc, argv);
|
||||||
a.setupTranslator();
|
a.setupTranslator();
|
||||||
// reset QT_QPA_PLATFORM to a sane value for any processes started from KWin
|
// reset QT_QPA_PLATFORM to a sane value for any processes started from KWin
|
||||||
|
|
|
@ -47,6 +47,7 @@ BackingStore::BackingStore(QWindow *w, KWayland::Client::ShmPool *shm)
|
||||||
}
|
}
|
||||||
const QSize size = m_backBuffer.size();
|
const QSize size = m_backBuffer.size();
|
||||||
m_backBuffer = QImage(b->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied);
|
m_backBuffer = QImage(b->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied);
|
||||||
|
m_backBuffer.setDevicePixelRatio(scale());
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -61,7 +62,7 @@ QPaintDevice *BackingStore::paintDevice()
|
||||||
void BackingStore::resize(const QSize &size, const QRegion &staticContents)
|
void BackingStore::resize(const QSize &size, const QRegion &staticContents)
|
||||||
{
|
{
|
||||||
Q_UNUSED(staticContents)
|
Q_UNUSED(staticContents)
|
||||||
m_size = size;
|
m_size = size * scale();
|
||||||
if (!m_buffer) {
|
if (!m_buffer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -73,13 +74,15 @@ void BackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &o
|
||||||
{
|
{
|
||||||
Q_UNUSED(region)
|
Q_UNUSED(region)
|
||||||
Q_UNUSED(offset)
|
Q_UNUSED(offset)
|
||||||
auto s = static_cast<Window *>(window->handle())->surface();
|
|
||||||
|
auto w = static_cast<Window *>(window->handle());
|
||||||
|
auto s = w->surface();
|
||||||
if (!s) {
|
if (!s) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
s->attachBuffer(m_buffer);
|
s->attachBuffer(m_buffer);
|
||||||
// TODO: proper damage region
|
// TODO: proper damage region
|
||||||
s->damage(QRect(QPoint(0, 0), m_backBuffer.size()));
|
s->damage(QRect(QPoint(0, 0), m_backBuffer.size() / scale()));
|
||||||
s->commit(KWayland::Client::Surface::CommitFlag::None);
|
s->commit(KWayland::Client::Surface::CommitFlag::None);
|
||||||
waylandServer()->internalClientConection()->flush();
|
waylandServer()->internalClientConection()->flush();
|
||||||
waylandServer()->dispatch();
|
waylandServer()->dispatch();
|
||||||
|
@ -108,6 +111,7 @@ void BackingStore::beginPaint(const QRegion&)
|
||||||
auto b = m_buffer.toStrongRef();
|
auto b = m_buffer.toStrongRef();
|
||||||
b->setUsed(true);
|
b->setUsed(true);
|
||||||
m_backBuffer = QImage(b->address(), m_size.width(), m_size.height(), QImage::Format_ARGB32_Premultiplied);
|
m_backBuffer = QImage(b->address(), m_size.width(), m_size.height(), QImage::Format_ARGB32_Premultiplied);
|
||||||
|
m_backBuffer.setDevicePixelRatio(scale());
|
||||||
if (oldBuffer) {
|
if (oldBuffer) {
|
||||||
b->copy(oldBuffer->address());
|
b->copy(oldBuffer->address());
|
||||||
} else {
|
} else {
|
||||||
|
@ -115,5 +119,10 @@ void BackingStore::beginPaint(const QRegion&)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int BackingStore::scale() const
|
||||||
|
{
|
||||||
|
return static_cast<Window *>(window()->handle())->scale();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ public:
|
||||||
void beginPaint(const QRegion &) override;
|
void beginPaint(const QRegion &) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
int scale() const;
|
||||||
KWayland::Client::ShmPool *m_shm;
|
KWayland::Client::ShmPool *m_shm;
|
||||||
QWeakPointer<KWayland::Client::Buffer> m_buffer;
|
QWeakPointer<KWayland::Client::Buffer> m_buffer;
|
||||||
QImage m_backBuffer;
|
QImage m_backBuffer;
|
||||||
|
|
|
@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#define WL_EGL_PLATFORM 1
|
#define WL_EGL_PLATFORM 1
|
||||||
#include "integration.h"
|
#include "integration.h"
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
#include "screens.h"
|
||||||
#include "../../shell_client.h"
|
#include "../../shell_client.h"
|
||||||
#include "../../wayland_server.h"
|
#include "../../wayland_server.h"
|
||||||
#include <logging.h>
|
#include <logging.h>
|
||||||
|
@ -44,7 +45,10 @@ Window::Window(QWindow *window, KWayland::Client::Surface *surface, KWayland::Cl
|
||||||
, m_shellSurface(shellSurface)
|
, m_shellSurface(shellSurface)
|
||||||
, m_windowId(++s_windowId)
|
, m_windowId(++s_windowId)
|
||||||
, m_integration(integration)
|
, m_integration(integration)
|
||||||
|
, m_scale(screens()->maxScale())
|
||||||
{
|
{
|
||||||
|
m_surface->setScale(m_scale);
|
||||||
|
|
||||||
QObject::connect(m_surface, &QObject::destroyed, window, [this] { m_surface = nullptr;});
|
QObject::connect(m_surface, &QObject::destroyed, window, [this] { m_surface = nullptr;});
|
||||||
QObject::connect(m_shellSurface, &QObject::destroyed, window, [this] { m_shellSurface = nullptr;});
|
QObject::connect(m_shellSurface, &QObject::destroyed, window, [this] { m_shellSurface = nullptr;});
|
||||||
waylandServer()->internalClientConection()->flush();
|
waylandServer()->internalClientConection()->flush();
|
||||||
|
@ -94,14 +98,17 @@ void Window::setGeometry(const QRect &rect)
|
||||||
if (rect.height() != oldRect.height()) {
|
if (rect.height() != oldRect.height()) {
|
||||||
emit window()->heightChanged(rect.height());
|
emit window()->heightChanged(rect.height());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QSize nativeSize = rect.size() * m_scale;
|
||||||
|
|
||||||
if (m_contentFBO) {
|
if (m_contentFBO) {
|
||||||
if (m_contentFBO->width() != geometry().width() || m_contentFBO->height() != geometry().height()) {
|
if (m_contentFBO->size() != nativeSize) {
|
||||||
m_resized = true;
|
m_resized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if HAVE_WAYLAND_EGL
|
#if HAVE_WAYLAND_EGL
|
||||||
if (m_eglWaylandWindow) {
|
if (m_eglWaylandWindow) {
|
||||||
wl_egl_window_resize(m_eglWaylandWindow, geometry().width(), geometry().height(), 0, 0);
|
wl_egl_window_resize(m_eglWaylandWindow, nativeSize.width(), nativeSize.height(), 0, 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
QWindowSystemInterface::handleGeometryChange(window(), geometry());
|
QWindowSystemInterface::handleGeometryChange(window(), geometry());
|
||||||
|
@ -124,7 +131,7 @@ void Window::unmap()
|
||||||
void Window::createEglSurface(EGLDisplay dpy, EGLConfig config)
|
void Window::createEglSurface(EGLDisplay dpy, EGLConfig config)
|
||||||
{
|
{
|
||||||
#if HAVE_WAYLAND_EGL
|
#if HAVE_WAYLAND_EGL
|
||||||
const QSize size = window()->size();
|
const QSize size = window()->size() * m_scale;
|
||||||
m_eglWaylandWindow = wl_egl_window_create(*m_surface, size.width(), size.height());
|
m_eglWaylandWindow = wl_egl_window_create(*m_surface, size.width(), size.height());
|
||||||
if (!m_eglWaylandWindow) {
|
if (!m_eglWaylandWindow) {
|
||||||
return;
|
return;
|
||||||
|
@ -158,7 +165,8 @@ void Window::createFBO()
|
||||||
if (m_contentFBO && r.size().isEmpty()) {
|
if (m_contentFBO && r.size().isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_contentFBO.reset(new QOpenGLFramebufferObject(r.width(), r.height(), QOpenGLFramebufferObject::CombinedDepthStencil));
|
const QSize nativeSize = r.size() * m_scale;
|
||||||
|
m_contentFBO.reset(new QOpenGLFramebufferObject(nativeSize.width(), nativeSize.height(), QOpenGLFramebufferObject::CombinedDepthStencil));
|
||||||
if (!m_contentFBO->isValid()) {
|
if (!m_contentFBO->isValid()) {
|
||||||
qCWarning(KWIN_QPA) << "Content FBO is not valid";
|
qCWarning(KWIN_QPA) << "Content FBO is not valid";
|
||||||
}
|
}
|
||||||
|
@ -174,5 +182,15 @@ ShellClient *Window::shellClient()
|
||||||
return m_shellClient;
|
return m_shellClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Window::scale() const
|
||||||
|
{
|
||||||
|
return m_scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal Window::devicePixelRatio() const
|
||||||
|
{
|
||||||
|
return m_scale;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,9 @@ public:
|
||||||
}
|
}
|
||||||
void createEglSurface(EGLDisplay dpy, EGLConfig config);
|
void createEglSurface(EGLDisplay dpy, EGLConfig config);
|
||||||
|
|
||||||
|
int scale() const;
|
||||||
|
qreal devicePixelRatio() const override;
|
||||||
|
|
||||||
void bindContentFBO();
|
void bindContentFBO();
|
||||||
const QSharedPointer<QOpenGLFramebufferObject> &contentFBO() const {
|
const QSharedPointer<QOpenGLFramebufferObject> &contentFBO() const {
|
||||||
return m_contentFBO;
|
return m_contentFBO;
|
||||||
|
@ -96,6 +99,7 @@ private:
|
||||||
#endif
|
#endif
|
||||||
quint32 m_windowId;
|
quint32 m_windowId;
|
||||||
const Integration *m_integration;
|
const Integration *m_integration;
|
||||||
|
int m_scale = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -510,9 +510,7 @@ void ShellClient::setInternalFramebufferObject(const QSharedPointer<QOpenGLFrame
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Kwin currently scales internal windows to 1, so this is currently always correct
|
m_clientSize = fbo->size() / surface()->scale();
|
||||||
//when that changes, this needs adjusting
|
|
||||||
m_clientSize = fbo->size();
|
|
||||||
markAsMapped();
|
markAsMapped();
|
||||||
doSetGeometry(QRect(geom.topLeft(), m_clientSize));
|
doSetGeometry(QRect(geom.topLeft(), m_clientSize));
|
||||||
Toplevel::setInternalFramebufferObject(fbo);
|
Toplevel::setInternalFramebufferObject(fbo);
|
||||||
|
|
Loading…
Reference in a new issue