File indexing completed on 2024-05-19 16:34:47

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