diff --git a/client.cpp b/client.cpp --- a/client.cpp +++ b/client.cpp @@ -645,7 +645,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); @@ -694,9 +694,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 @@ -73,8 +73,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); } @@ -310,10 +310,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); } } @@ -898,7 +899,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); } } @@ -1573,10 +1574,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; @@ -2582,7 +2583,7 @@ // something with Enter/LeaveNotify events, looks like XFree performance problem or something *shrug* // (https://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, nullptr, rootWindow()); + m_moveResizeGrabWindow.create(Xcb::scX(r), XCB_WINDOW_CLASS_INPUT_ONLY, 0, nullptr, rootWindow()); m_moveResizeGrabWindow.map(); m_moveResizeGrabWindow.raise(); updateXTime(); diff --git a/main.h b/main.h --- a/main.h +++ b/main.h @@ -170,6 +170,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; @@ -239,6 +249,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 @@ -56,6 +56,8 @@ return m_environment; } + void setX11Scale(int scale) override; + protected: void performStartup() override; 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 "xwl/xwayland.h" @@ -145,6 +146,14 @@ destroyCompositor(); } +void ApplicationWayland::setX11Scale(int scale) { + // TODO: change this to actual release number + if (!m_xwayland || m_xwayland->releaseNumber() < 12099000) { + return; + } + m_x11Scale = scale; +} + void ApplicationWayland::performStartup() { if (m_startXWayland) { 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 @@ -209,15 +210,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 @@ -328,6 +328,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 @@ -1178,7 +1178,6 @@ filter = ImageFilterGood; else filter = ImageFilterFast; - s_frameTexture->setFilter(filter == ImageFilterGood ? GL_LINEAR : GL_NEAREST); } 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 @@ -274,14 +274,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 @@ -757,8 +757,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 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. * @@ -564,7 +595,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)); } }; @@ -919,27 +950,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()) { @@ -952,14 +983,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: @@ -1492,9 +1523,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; } @@ -1521,7 +1553,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); } @@ -1555,7 +1587,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); } @@ -1596,7 +1628,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 @@ -1623,6 +1655,7 @@ if (!isValid()) { return; } + width = scX(width); xcb_configure_window(connection(), m_window, XCB_CONFIG_WINDOW_BORDER_WIDTH, &width); } @@ -1693,12 +1726,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); } @@ -1711,7 +1745,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); } @@ -1723,9 +1757,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; @@ -1777,11 +1812,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; } diff --git a/xwl/xwayland.h b/xwl/xwayland.h --- a/xwl/xwayland.h +++ b/xwl/xwayland.h @@ -55,6 +55,9 @@ const xcb_query_extension_reply_t *xfixes() const { return m_xfixes; } + uint32_t releaseNumber() const { + return m_releaseNumber; + } Q_SIGNALS: void initialized(); @@ -75,6 +78,7 @@ DataBridge *m_dataBridge = nullptr; ApplicationWaylandAbstract *m_app; + uint32_t m_releaseNumber = 0; Q_DISABLE_COPY(Xwayland) }; diff --git a/xwl/xwayland.cpp b/xwl/xwayland.cpp --- a/xwl/xwayland.cpp +++ b/xwl/xwayland.cpp @@ -150,11 +150,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, [this] (QProcess::ProcessError error) { if (error == QProcess::FailedToStart) { @@ -218,6 +225,13 @@ Q_EMIT criticalError(1); 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_releaseNumber = xcb_get_setup(xcbConn)->release_number; + QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(xcbConn), QSocketNotifier::Read, this); auto processXcbEvents = [this, xcbConn] { while (auto event = xcb_poll_for_event(xcbConn)) {