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