File indexing completed on 2024-05-19 05:32:21

0001 /*
0002     SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "scene/surfaceitem_x11.h"
0008 #include "compositor_x11.h"
0009 #include "core/renderbackend.h"
0010 #include "x11syncmanager.h"
0011 #include "x11window.h"
0012 
0013 namespace KWin
0014 {
0015 
0016 SurfaceItemX11::SurfaceItemX11(X11Window *window, Scene *scene, Item *parent)
0017     : SurfaceItem(scene, parent)
0018     , m_window(window)
0019 {
0020     connect(window, &Window::bufferGeometryChanged,
0021             this, &SurfaceItemX11::handleBufferGeometryChanged);
0022     connect(window, &X11Window::shapeChanged,
0023             this, &SurfaceItemX11::handleShapeChanged);
0024 
0025     m_damageHandle = xcb_generate_id(kwinApp()->x11Connection());
0026     xcb_damage_create(kwinApp()->x11Connection(), m_damageHandle, window->frameId(),
0027                       XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
0028 
0029     // With unmanaged windows there is a race condition between the client painting the window
0030     // and us setting up damage tracking.  If the client wins we won't get a damage event even
0031     // though the window has been painted.  To avoid this we mark the whole window as damaged
0032     // immediately after creating the damage object.
0033     if (window->isUnmanaged()) {
0034         m_isDamaged = true;
0035     }
0036 
0037     setDestinationSize(window->bufferGeometry().size());
0038     setBufferSourceBox(QRectF(QPointF(0, 0), window->bufferGeometry().size()));
0039     setBufferSize(window->bufferGeometry().size().toSize());
0040 }
0041 
0042 SurfaceItemX11::~SurfaceItemX11()
0043 {
0044     destroyDamage();
0045 }
0046 
0047 X11Window *SurfaceItemX11::window() const
0048 {
0049     return m_window;
0050 }
0051 
0052 void SurfaceItemX11::preprocess()
0053 {
0054     if (!damage().isEmpty()) {
0055         X11Compositor *compositor = X11Compositor::self();
0056         if (X11SyncManager *syncManager = compositor->syncManager()) {
0057             syncManager->insertWait();
0058         }
0059     }
0060     SurfaceItem::preprocess();
0061 }
0062 
0063 void SurfaceItemX11::processDamage()
0064 {
0065     m_isDamaged = true;
0066     scheduleFrame();
0067 }
0068 
0069 bool SurfaceItemX11::fetchDamage()
0070 {
0071     if (!m_isDamaged) {
0072         return false;
0073     }
0074 
0075     if (m_damageHandle == XCB_NONE) {
0076         return true;
0077     }
0078 
0079     xcb_xfixes_region_t region = xcb_generate_id(kwinApp()->x11Connection());
0080     xcb_xfixes_create_region(kwinApp()->x11Connection(), region, 0, nullptr);
0081     xcb_damage_subtract(kwinApp()->x11Connection(), m_damageHandle, 0, region);
0082 
0083     m_damageCookie = xcb_xfixes_fetch_region_unchecked(kwinApp()->x11Connection(), region);
0084     xcb_xfixes_destroy_region(kwinApp()->x11Connection(), region);
0085 
0086     m_havePendingDamageRegion = true;
0087 
0088     return true;
0089 }
0090 
0091 void SurfaceItemX11::waitForDamage()
0092 {
0093     if (!m_havePendingDamageRegion) {
0094         return;
0095     }
0096     m_havePendingDamageRegion = false;
0097 
0098     xcb_xfixes_fetch_region_reply_t *reply =
0099         xcb_xfixes_fetch_region_reply(kwinApp()->x11Connection(), m_damageCookie, nullptr);
0100     if (!reply) {
0101         qCDebug(KWIN_CORE) << "Failed to check damage region";
0102         return;
0103     }
0104 
0105     const int rectCount = xcb_xfixes_fetch_region_rectangles_length(reply);
0106     QRegion region;
0107 
0108     if (rectCount > 1 && rectCount < 16) {
0109         xcb_rectangle_t *rects = xcb_xfixes_fetch_region_rectangles(reply);
0110 
0111         QList<QRect> qtRects;
0112         qtRects.reserve(rectCount);
0113 
0114         for (int i = 0; i < rectCount; ++i) {
0115             qtRects << QRect(rects[i].x, rects[i].y, rects[i].width, rects[i].height);
0116         }
0117         region.setRects(qtRects.constData(), rectCount);
0118     } else {
0119         region = QRect(reply->extents.x, reply->extents.y, reply->extents.width, reply->extents.height);
0120     }
0121     free(reply);
0122 
0123     addDamage(region);
0124     m_isDamaged = false;
0125 }
0126 
0127 void SurfaceItemX11::forgetDamage()
0128 {
0129     // If the window is destroyed, we cannot destroy XDamage handle. :/
0130     m_isDamaged = false;
0131     m_damageHandle = XCB_NONE;
0132 }
0133 
0134 void SurfaceItemX11::destroyDamage()
0135 {
0136     if (m_damageHandle != XCB_NONE) {
0137         m_isDamaged = false;
0138         xcb_damage_destroy(kwinApp()->x11Connection(), m_damageHandle);
0139         m_damageHandle = XCB_NONE;
0140     }
0141 }
0142 
0143 void SurfaceItemX11::handleBufferGeometryChanged()
0144 {
0145     setDestinationSize(m_window->bufferGeometry().size());
0146     setBufferSourceBox(QRectF(QPointF(0, 0), m_window->bufferGeometry().size()));
0147     setBufferSize(m_window->bufferGeometry().size().toSize());
0148 }
0149 
0150 void SurfaceItemX11::handleShapeChanged()
0151 {
0152     scheduleRepaint(boundingRect());
0153     discardQuads();
0154 }
0155 
0156 QList<QRectF> SurfaceItemX11::shape() const
0157 {
0158     const QRectF clipRect = m_window->clientGeometry().translated(-m_window->bufferGeometry().topLeft());
0159     QList<QRectF> shape = m_window->shapeRegion();
0160     // bounded to clipRect
0161     for (QRectF &shapePart : shape) {
0162         shapePart = shapePart.intersected(clipRect);
0163     }
0164     return shape;
0165 }
0166 
0167 QRegion SurfaceItemX11::opaque() const
0168 {
0169     QRegion shapeRegion;
0170     for (const QRectF &shapePart : shape()) {
0171         shapeRegion += shapePart.toRect();
0172     }
0173     if (!m_window->hasAlpha()) {
0174         return shapeRegion;
0175     } else {
0176         return m_window->opaqueRegion() & shapeRegion;
0177     }
0178     return QRegion();
0179 }
0180 
0181 std::unique_ptr<SurfacePixmap> SurfaceItemX11::createPixmap()
0182 {
0183     return std::make_unique<SurfacePixmapX11>(this);
0184 }
0185 
0186 SurfacePixmapX11::SurfacePixmapX11(SurfaceItemX11 *item, QObject *parent)
0187     : SurfacePixmap(Compositor::self()->backend()->createSurfaceTextureX11(this), parent)
0188     , m_item(item)
0189 {
0190 }
0191 
0192 SurfacePixmapX11::~SurfacePixmapX11()
0193 {
0194     if (m_pixmap != XCB_PIXMAP_NONE) {
0195         xcb_free_pixmap(kwinApp()->x11Connection(), m_pixmap);
0196     }
0197 }
0198 
0199 bool SurfacePixmapX11::isValid() const
0200 {
0201     return m_pixmap != XCB_PIXMAP_NONE;
0202 }
0203 
0204 xcb_pixmap_t SurfacePixmapX11::pixmap() const
0205 {
0206     return m_pixmap;
0207 }
0208 
0209 xcb_visualid_t SurfacePixmapX11::visual() const
0210 {
0211     return m_item->window()->visual();
0212 }
0213 
0214 void SurfacePixmapX11::create()
0215 {
0216     const X11Window *window = m_item->window();
0217     if (window->isDeleted()) {
0218         return;
0219     }
0220 
0221     XServerGrabber grabber;
0222     xcb_connection_t *connection = kwinApp()->x11Connection();
0223     xcb_window_t frame = window->frameId();
0224     xcb_pixmap_t pixmap = xcb_generate_id(connection);
0225     xcb_void_cookie_t namePixmapCookie = xcb_composite_name_window_pixmap_checked(connection,
0226                                                                                   frame,
0227                                                                                   pixmap);
0228     Xcb::WindowAttributes windowAttributes(frame);
0229     Xcb::WindowGeometry windowGeometry(frame);
0230     if (xcb_generic_error_t *error = xcb_request_check(connection, namePixmapCookie)) {
0231         qCDebug(KWIN_CORE, "Failed to create window pixmap for window 0x%x (error code %d)",
0232                 window->window(), error->error_code);
0233         free(error);
0234         return;
0235     }
0236     // check that the received pixmap is valid and actually matches what we
0237     // know about the window (i.e. size)
0238     if (!windowAttributes || windowAttributes->map_state != XCB_MAP_STATE_VIEWABLE) {
0239         qCDebug(KWIN_CORE, "Failed to create window pixmap for window 0x%x (not viewable)",
0240                 window->window());
0241         xcb_free_pixmap(connection, pixmap);
0242         return;
0243     }
0244     const QRectF bufferGeometry = window->bufferGeometry();
0245     if (windowGeometry.size() != bufferGeometry.size()) {
0246         qCDebug(KWIN_CORE, "Failed to create window pixmap for window 0x%x: window size (%fx%f) != buffer size (%fx%f)", window->window(),
0247                 windowGeometry.size().width(), windowGeometry.size().height(), bufferGeometry.width(), bufferGeometry.height());
0248         xcb_free_pixmap(connection, pixmap);
0249         return;
0250     }
0251 
0252     m_pixmap = pixmap;
0253     m_hasAlphaChannel = window->hasAlpha();
0254     // this class is only used on X11 where the logical size and
0255     // device pixel size is guaranteed to be the same and we can convert safely
0256     m_size = bufferGeometry.size().toSize();
0257 }
0258 
0259 } // namespace KWin
0260 
0261 #include "moc_surfaceitem_x11.cpp"