File indexing completed on 2024-11-10 04:57:16
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"