diff --git a/client.cpp b/client.cpp --- a/client.cpp +++ b/client.cpp @@ -618,7 +618,7 @@ } if (noBorder()) { xcb_shape_combine(connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, XCB_SHAPE_SK_BOUNDING, - frameId(), clientPos().x(), clientPos().y(), window()); + frameId(), Xcb::scX(clientPos().x()), Xcb::scX(clientPos().y()), window()); } } else if (app_noborder) { xcb_shape_mask(connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, frameId(), 0, 0, XCB_PIXMAP_NONE); @@ -667,9 +667,9 @@ xcb_shape_combine(c, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT, XCB_SHAPE_SK_BOUNDING, shape_helper_window, 0, 0, frameId()); xcb_shape_combine(c, XCB_SHAPE_SO_SUBTRACT, XCB_SHAPE_SK_INPUT, XCB_SHAPE_SK_BOUNDING, - shape_helper_window, clientPos().x(), clientPos().y(), window()); + shape_helper_window, Xcb::scX(clientPos().x()), Xcb::scX(clientPos().y()), window()); xcb_shape_combine(c, XCB_SHAPE_SO_UNION, XCB_SHAPE_SK_INPUT, XCB_SHAPE_SK_INPUT, - shape_helper_window, clientPos().x(), clientPos().y(), window()); + shape_helper_window, Xcb::scX(clientPos().x()), Xcb::scX(clientPos().y()), window()); xcb_shape_combine(c, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT, XCB_SHAPE_SK_INPUT, frameId(), 0, 0, shape_helper_window); } diff --git a/events.cpp b/events.cpp --- a/events.cpp +++ b/events.cpp @@ -691,7 +691,7 @@ } if (e->value_mask & (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_WIDTH)) - configureRequest(e->value_mask, e->x, e->y, e->width, e->height, 0, false); + configureRequest(e->value_mask, Xcb::scK(e->x), Xcb::scK(e->y), Xcb::scK(e->width), Xcb::scK(e->height), 0, false); if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) restackWindow(e->sibling, e->stack_mode, NET::FromApplication, userTime(), false); @@ -1066,6 +1066,11 @@ // return value matters only when filtering events before decoration gets them bool Client::motionNotifyEvent(xcb_window_t w, int state, int x, int y, int x_root, int y_root) { + x = Xcb::scK(x); + y = Xcb::scK(y); + x_root = Xcb::scK(x_root); + y_root = Xcb::scK(y_root); + if (w == frameId() && isDecorated() && !isMinimized()) { // TODO Mouse move event dependent on state QHoverEvent event(QEvent::HoverMove, QPointF(x, y), QPointF(x, y)); @@ -1296,7 +1301,7 @@ { if (effects) static_cast(effects)->checkInputWindowStacking(); // keep them on top - QRect newgeom(e->x, e->y, e->width, e->height); + const QRect newgeom = Xcb::scK(QRect(e->x, e->y, e->width, e->height)); if (newgeom != geom) { addWorkspaceRepaint(visibleRect()); // damage old area QRect old = geom; diff --git a/geometry.cpp b/geometry.cpp --- a/geometry.cpp +++ b/geometry.cpp @@ -72,8 +72,8 @@ QRect geom = screens()->geometry(); if (rootInfo()) { NETSize desktop_geometry; - desktop_geometry.width = geom.width(); - desktop_geometry.height = geom.height(); + desktop_geometry.width = Xcb::scX(geom.width()); + desktop_geometry.height = Xcb::scX(geom.height()); rootInfo()->setDesktopGeometry(desktop_geometry); } @@ -314,10 +314,11 @@ if (rootInfo()) { NETRect r; for (int i = 1; i <= numberOfDesktops; i++) { - r.pos.x = workarea[ i ].x(); - r.pos.y = workarea[ i ].y(); - r.size.width = workarea[ i ].width(); - r.size.height = workarea[ i ].height(); + const QRect xWorkarea = Xcb::scX(workarea[i]); + r.pos.x = xWorkarea.x(); + r.pos.y = xWorkarea.y(); + r.size.width = xWorkarea.width(); + r.size.height = xWorkarea.height(); rootInfo()->setWorkArea(i, r); } } @@ -911,7 +912,7 @@ // left and top needed due to narrowing conversations restrictions in C++11 const uint32_t left = frame.left; const uint32_t top = frame.top; - const uint32_t values[] = { geometry->x - left, geometry->y - top }; + const uint32_t values[] = { Xcb::scX(geometry->x - left), Xcb::scX(geometry->y - top) }; xcb_configure_window(connection(), w, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values); } } @@ -1600,10 +1601,10 @@ c.response_type = XCB_CONFIGURE_NOTIFY; c.event = window(); c.window = window(); - c.x = x() + clientPos().x(); - c.y = y() + clientPos().y(); - c.width = clientSize().width(); - c.height = clientSize().height(); + c.x = Xcb::scX(x() + clientPos().x()); + c.y = Xcb::scX(y() + clientPos().y()); + c.width = Xcb::scX(clientSize().width()); + c.height = Xcb::scX(clientSize().height()); c.border_width = 0; c.above_sibling = XCB_WINDOW_NONE; c.override_redirect = 0; @@ -2722,7 +2723,7 @@ // something with Enter/LeaveNotify events, looks like XFree performance problem or something *shrug* // (http://lists.kde.org/?t=107302193400001&r=1&w=2) QRect r = workspace()->clientArea(FullArea, this); - m_moveResizeGrabWindow.create(r, XCB_WINDOW_CLASS_INPUT_ONLY, 0, NULL, rootWindow()); + m_moveResizeGrabWindow.create(Xcb::scX(r), XCB_WINDOW_CLASS_INPUT_ONLY, 0, NULL, rootWindow()); m_moveResizeGrabWindow.map(); m_moveResizeGrabWindow.raise(); updateXTime(); diff --git a/main.h b/main.h --- a/main.h +++ b/main.h @@ -172,6 +172,16 @@ return m_connection; } + /** + * @returns the X11 global scale factor + **/ + int x11Scale() const { + return m_x11Scale; + } + virtual void setX11Scale(int scale) { + Q_UNUSED(scale) + } + #ifdef KWIN_BUILD_ACTIVITIES bool usesKActivities() const { return m_useKActivities; @@ -242,6 +252,7 @@ protected: QString m_originalSessionKey; static int crashes; + int m_x11Scale = 1; private Q_SLOTS: void resetCrashesCount(); diff --git a/main_wayland.h b/main_wayland.h --- a/main_wayland.h +++ b/main_wayland.h @@ -54,6 +54,8 @@ return m_environment; } + void setX11Scale(int scale) override; + protected: void performStartup() override; @@ -74,6 +76,7 @@ QMetaObject::Connection m_xwaylandFailConnection; QProcessEnvironment m_environment; QString m_sessionArgument; + uint32_t m_xwaylandReleaseNumber = 0; }; } diff --git a/main_wayland.cpp b/main_wayland.cpp --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -25,6 +25,7 @@ // kwin #include "platform.h" #include "effects.h" +#include "screens.h" #include "tabletmodemanager.h" #include "wayland_server.h" #include "xcbutils.h" @@ -165,6 +166,14 @@ destroyCompositor(); } +void ApplicationWayland::setX11Scale(int scale) { + // TODO: change this to actual release number + if (m_xwaylandReleaseNumber < 12099000) { + return; + } + m_x11Scale = scale; +} + void ApplicationWayland::performStartup() { if (m_startXWayland) { @@ -226,6 +235,14 @@ // about to quit return; } + + // Query the XWayland release number to decide if multi dpi support + // should be enabled. For that force Screens to do a size update + // TODO: better do this earlier when we provide the arguments for + // Xwayland binary? + m_xwaylandReleaseNumber = xcb_get_setup(c)->release_number; + emit Screens::self()->changed(); + QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(c), QSocketNotifier::Read, this); auto processXcbEvents = [this, c] { while (auto event = xcb_poll_for_event(c)) { @@ -382,11 +399,18 @@ env.insert("WAYLAND_SOCKET", QByteArray::number(wlfd)); env.insert("EGL_PLATFORM", QByteArrayLiteral("DRM")); m_xwaylandProcess->setProcessEnvironment(env); - m_xwaylandProcess->setArguments({QStringLiteral("-displayfd"), - QString::number(pipeFds[1]), - QStringLiteral("-rootless"), - QStringLiteral("-wm"), - QString::number(fd)}); + + QStringList arguments = {QStringLiteral("-displayfd"), + QString::number(pipeFds[1]), + QStringLiteral("-rootless"), + QStringLiteral("-wm"), + QString::number(fd)}; + + // TODO: Make this dependent on the release number. pkgconfig? + arguments.append(QStringLiteral("-maxFactorRescale")); + + m_xwaylandProcess->setArguments(arguments); + m_xwaylandFailConnection = connect(m_xwaylandProcess, static_cast(&QProcess::error), this, [] (QProcess::ProcessError error) { if (error == QProcess::FailedToStart) { diff --git a/netinfo.cpp b/netinfo.cpp --- a/netinfo.cpp +++ b/netinfo.cpp @@ -26,6 +26,7 @@ #include "rootinfo_filter.h" #include "virtualdesktops.h" #include "workspace.h" +#include "xcbutils.h" // Qt #include @@ -208,15 +209,16 @@ Client* c = Workspace::self()->findClient(Predicate::WindowMatch, w); if (c) { updateXTime(); // otherwise grabbing may have old timestamp - this message should include timestamp - c->NETMoveResize(x_root, y_root, (Direction)direction); + c->NETMoveResize(Xcb::scK(x_root), Xcb::scK(y_root), (Direction)direction); } } void RootInfo::moveResizeWindow(xcb_window_t w, int flags, int x, int y, int width, int height) { Client* c = Workspace::self()->findClient(Predicate::WindowMatch, w); + const QRect geo = Xcb::scK(QRect(x, y, width, height)); if (c) - c->NETMoveResizeWindow(flags, x, y, width, height); + c->NETMoveResizeWindow(flags, geo.x(), geo.y(), geo.width(), geo.height()); } void RootInfo::gotPing(xcb_window_t w, xcb_timestamp_t timestamp) diff --git a/platformsupport/scenes/opengl/abstract_egl_backend.cpp b/platformsupport/scenes/opengl/abstract_egl_backend.cpp --- a/platformsupport/scenes/opengl/abstract_egl_backend.cpp +++ b/platformsupport/scenes/opengl/abstract_egl_backend.cpp @@ -325,6 +325,7 @@ , m_image(EGL_NO_IMAGE_KHR) { m_target = GL_TEXTURE_2D; + m_canUseMipmaps = true; } AbstractEglTexture::~AbstractEglTexture() diff --git a/plugins/scenes/opengl/scene_opengl.cpp b/plugins/scenes/opengl/scene_opengl.cpp --- a/plugins/scenes/opengl/scene_opengl.cpp +++ b/plugins/scenes/opengl/scene_opengl.cpp @@ -1160,13 +1160,18 @@ } // Update the texture filter - if (options->glSmoothScale() != 0 && - (mask & (PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED))) + if (waylandServer()) { filter = ImageFilterGood; - else - filter = ImageFilterFast; - - s_frameTexture->setFilter(filter == ImageFilterGood ? GL_LINEAR : GL_NEAREST); + s_frameTexture->setFilter(GL_LINEAR); + } else { + if (options->glSmoothScale() != 0 && + (mask & (PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED))) { + filter = ImageFilterGood; + } else { + filter = ImageFilterFast; + } + s_frameTexture->setFilter(filter == ImageFilterGood ? GL_LINEAR : GL_NEAREST); + } const GLVertexAttrib attribs[] = { { VA_Position, 2, GL_FLOAT, offsetof(GLVertex2D, position) }, @@ -1364,8 +1369,18 @@ shader->setUniform(GLShader::Saturation, data.saturation()); - const GLenum filter = (mask & (Effect::PAINT_WINDOW_TRANSFORMED | Effect::PAINT_SCREEN_TRANSFORMED)) - && options->glSmoothScale() != 0 ? GL_LINEAR : GL_NEAREST; + GLenum textureFilter; + if (waylandServer()) { + textureFilter = GL_LINEAR; + } else { + const bool isTransformed = mask & (Effect::PAINT_WINDOW_TRANSFORMED | + Effect::PAINT_SCREEN_TRANSFORMED); + if (isTransformed && options->glSmoothScale() != 0) { + textureFilter = GL_LINEAR; + } else { + textureFilter = GL_NEAREST; + } + } WindowQuadList quads[LeafCount]; @@ -1459,15 +1474,19 @@ opacity = nodes[i].opacity; } - nodes[i].texture->setFilter(filter); + nodes[i].texture->setFilter(textureFilter); nodes[i].texture->setWrapMode(GL_CLAMP_TO_EDGE); nodes[i].texture->bind(); vbo->draw(region, primitiveType, nodes[i].firstVertex, nodes[i].vertexCount, m_hardwareClipping); } vbo->unbindArrays(); + if (textureFilter = GL_LINEAR) { + s_frameTexture->generateMipmaps(); + } + // render sub-surfaces auto wp = windowPixmap(); const auto &children = wp ? wp->children() : QVector(); diff --git a/plugins/scenes/qpainter/scene_qpainter.cpp b/plugins/scenes/qpainter/scene_qpainter.cpp --- a/plugins/scenes/qpainter/scene_qpainter.cpp +++ b/plugins/scenes/qpainter/scene_qpainter.cpp @@ -272,14 +272,20 @@ renderShadow(painter); renderWindowDecorations(painter); + // render content const QRect target = QRect(toplevel->clientPos(), toplevel->clientSize()); - QSize srcSize = pixmap->image().size(); - if (pixmap->surface() && pixmap->surface()->scale() == 1 && srcSize != toplevel->clientSize()) { + + QRect src; + if (qobject_cast(toplevel)) { // special case for XWayland windows - srcSize = toplevel->clientSize(); + const auto scale = pixmap->surface()->scale(); + src = QRect((toplevel->clientPos() + toplevel->clientContentPos()) * scale, toplevel->clientSize() * scale); + painter->setRenderHint(QPainter::SmoothPixmapTransform); + } else { + src = QRect(toplevel->clientPos() + toplevel->clientContentPos(), pixmap->image().size()); } - const QRect src = QRect(toplevel->clientPos() + toplevel->clientContentPos(), srcSize); + painter->drawImage(target, pixmap->image(), src); // render subsurfaces diff --git a/scene.cpp b/scene.cpp --- a/scene.cpp +++ b/scene.cpp @@ -754,8 +754,8 @@ for (int i = 0; i < xcb_shape_get_rectangles_rectangles_length(reply.data()); ++i) - shape_region += QRegion(rects[ i ].x, rects[ i ].y, - rects[ i ].width, rects[ i ].height); + shape_region += QRegion(Xcb::scK(rects[ i ].x), Xcb::scK(rects[ i ].y), + Xcb::scK(rects[ i ].width), Xcb::scK(rects[ i ].height)); // make sure the shape is sane (X is async, maybe even XShape is broken) shape_region &= QRegion(0, 0, width(), height()); } else @@ -844,8 +844,8 @@ return *cached_quad_list; WindowQuadList ret; qreal scale = 1.0; - if (toplevel->surface()) { - scale = toplevel->surface()->scale(); + if (auto s = toplevel->surface()) { + scale = s->scale(); } if (toplevel->clientPos() == QPoint(0, 0) && toplevel->clientSize() == toplevel->decorationRect().size()) diff --git a/screens.cpp b/screens.cpp --- a/screens.cpp +++ b/screens.cpp @@ -32,6 +32,8 @@ #include #endif +#include + namespace KWin { @@ -139,6 +141,7 @@ m_boundingSize = bounding.size(); emit sizeChanged(); } + kwinApp()->setX11Scale(std::ceil(maxScale - 0.05)); if (!qFuzzyCompare(m_maxScale, maxScale)) { m_maxScale = maxScale; emit maxScaleChanged(); diff --git a/xcbutils.h b/xcbutils.h --- a/xcbutils.h +++ b/xcbutils.h @@ -52,6 +52,37 @@ static void lowerWindow(xcb_window_t window); static void selectInput(xcb_window_t window, uint32_t events); +template +static T scX(T arg) +{ + return arg * kwinApp()->x11Scale(); +} + +template +static T scK(T arg) +{ + auto ret = arg / (double)kwinApp()->x11Scale() + 0.5; + T ret2 = ret; + return ret2; +} + +template <> +QSize scK(QSize arg) +{ + return QSize(scK(arg.width()), scK(arg.height())); +} + +template <> +QRect scX(QRect arg) +{ + return QRect(scX(arg.x()), scX(arg.y()), scX(arg.width()), scX(arg.height())); +} +template <> +QRect scK(QRect arg) +{ + return QRect(scK(arg.x()), scK(arg.y()), scK(arg.width()), scK(arg.height())); +} + /** * @brief Variadic template to wrap an xcb request. * @@ -565,7 +596,7 @@ if (!geometry) { return QRect(); } - return QRect(geometry->x, geometry->y, geometry->width, geometry->height); + return scK(QRect(geometry->x, geometry->y, geometry->width, geometry->height)); } }; @@ -920,27 +951,27 @@ if (!hasMaxSize()) { return QSize(INT_MAX, INT_MAX); } - return QSize(qMax(m_sizeHints->maxWidth, 1), qMax(m_sizeHints->maxHeight, 1)); + return scK(QSize(qMax(m_sizeHints->maxWidth, 1), qMax(m_sizeHints->maxHeight, 1))); } QSize minSize() const { if (!hasMinSize()) { // according to ICCCM 4.1.23 base size should be used as a fallback return baseSize(); } - return QSize(m_sizeHints->minWidth, m_sizeHints->minHeight); + return scK(QSize(m_sizeHints->minWidth, m_sizeHints->minHeight)); } QSize baseSize() const { // Note: not using minSize as fallback if (!hasBaseSize()) { return QSize(0, 0); } - return QSize(m_sizeHints->baseWidth, m_sizeHints->baseHeight); + return scK(QSize(m_sizeHints->baseWidth, m_sizeHints->baseHeight)); } QSize resizeIncrements() const { if (!hasResizeIncrements()) { return QSize(1, 1); } - return QSize(qMax(m_sizeHints->widthInc, 1), qMax(m_sizeHints->heightInc, 1)); + return scK(QSize(qMax(m_sizeHints->widthInc, 1), qMax(m_sizeHints->heightInc, 1))); } xcb_gravity_t windowGravity() const { if (!hasWindowGravity()) { @@ -953,14 +984,14 @@ return QSize(1, INT_MAX); } // prevent devision by zero - return QSize(m_sizeHints->minAspect[0], qMax(m_sizeHints->minAspect[1], 1)); + return scK(QSize(m_sizeHints->minAspect[0], qMax(m_sizeHints->minAspect[1], 1))); } QSize maxAspect() const { if (!hasAspect()) { return QSize(INT_MAX, 1); } // prevent devision by zero - return QSize(m_sizeHints->maxAspect[0], qMax(m_sizeHints->maxAspect[1], 1)); + return scK(QSize(m_sizeHints->maxAspect[0], qMax(m_sizeHints->maxAspect[1], 1))); } private: @@ -1493,9 +1524,10 @@ xcb_window_t Window::doCreate(const QRect &geometry, uint16_t windowClass, uint32_t mask, const uint32_t *values, xcb_window_t parent) { m_logicGeometry = geometry; + const QRect xGeo = scX(geometry); xcb_window_t w = xcb_generate_id(connection()); xcb_create_window(connection(), XCB_COPY_FROM_PARENT, w, parent, - geometry.x(), geometry.y(), geometry.width(), geometry.height(), + xGeo.x(), xGeo.y(), xGeo.width(), xGeo.height(), 0, windowClass, XCB_COPY_FROM_PARENT, mask, values); return w; } @@ -1522,7 +1554,7 @@ return; } const uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; - const uint32_t values[] = { x, y, width, height }; + const uint32_t values[] = {scX(x), scX(y), scX(width), scX(height) }; xcb_configure_window(connection(), m_window, mask, values); } @@ -1556,7 +1588,7 @@ return; } const uint16_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; - const uint32_t values[] = { width, height }; + const uint32_t values[] = { scX(width), scX(height) }; xcb_configure_window(connection(), m_window, mask, values); } @@ -1597,7 +1629,7 @@ if (!isValid()) { return; } - xcb_reparent_window(connection(), m_window, parent, x, y); + xcb_reparent_window(connection(), m_window, parent, scX(x), scX(y)); } inline @@ -1624,6 +1656,7 @@ if (!isValid()) { return; } + width = scX(width); xcb_configure_window(connection(), m_window, XCB_CONFIG_WINDOW_BORDER_WIDTH, &width); } @@ -1694,12 +1727,13 @@ // helper functions static inline void moveResizeWindow(WindowId window, const QRect &geometry) { + QRect xGeo = scX(geometry); const uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; const uint32_t values[] = { - static_cast(geometry.x()), - static_cast(geometry.y()), - static_cast(geometry.width()), - static_cast(geometry.height()) + static_cast(xGeo.x()), + static_cast(xGeo.y()), + static_cast(xGeo.width()), + static_cast(xGeo.height()) }; xcb_configure_window(connection(), window, mask, values); } @@ -1712,7 +1746,7 @@ static inline void moveWindow(xcb_window_t window, uint32_t x, uint32_t y) { const uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y; - const uint32_t values[] = { x, y }; + const uint32_t values[] = { scX(x), scX(y) }; xcb_configure_window(connection(), window, mask, values); } @@ -1724,9 +1758,10 @@ static inline WindowId createInputWindow(const QRect &geometry, uint32_t mask, const uint32_t *values) { + const QRect xGeo = scX(geometry); WindowId window = xcb_generate_id(connection()); xcb_create_window(connection(), 0, window, rootWindow(), - geometry.x(), geometry.y(), geometry.width(), geometry.height(), + xGeo.x(), xGeo.y(), xGeo.width(), xGeo.height(), 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, mask, values); return window; @@ -1778,11 +1813,12 @@ static inline xcb_rectangle_t fromQt(const QRect &rect) { + const QRect rectSc = scX(rect); xcb_rectangle_t rectangle; - rectangle.x = rect.x(); - rectangle.y = rect.y(); - rectangle.width = rect.width(); - rectangle.height = rect.height(); + rectangle.x = rectSc.x(); + rectangle.y = rectSc.y(); + rectangle.width = rectSc.width(); + rectangle.height = rectSc.height(); return rectangle; }