File indexing completed on 2025-04-27 11:33:00
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2011 Arthur Arlt <a.arlt@stud.uni-heidelberg.de> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "x11_standalone_overlaywindow.h" 0011 0012 #include "composite.h" 0013 #include "kwinglobals.h" 0014 #include "scene/workspacescene.h" 0015 #include "utils/common.h" 0016 #include "utils/xcbutils.h" 0017 #include "workspace.h" 0018 0019 #include <QVector> 0020 0021 #include <xcb/composite.h> 0022 #include <xcb/shape.h> 0023 #if XCB_COMPOSITE_MAJOR_VERSION > 0 || XCB_COMPOSITE_MINOR_VERSION >= 3 0024 #define KWIN_HAVE_XCOMPOSITE_OVERLAY 0025 #endif 0026 0027 namespace KWin 0028 { 0029 OverlayWindowX11::OverlayWindowX11() 0030 : OverlayWindow() 0031 , X11EventFilter(QVector<int>{XCB_EXPOSE, XCB_VISIBILITY_NOTIFY}) 0032 , m_visible(true) 0033 , m_shown(false) 0034 , m_window(XCB_WINDOW_NONE) 0035 { 0036 } 0037 0038 OverlayWindowX11::~OverlayWindowX11() 0039 { 0040 } 0041 0042 bool OverlayWindowX11::create() 0043 { 0044 Q_ASSERT(m_window == XCB_WINDOW_NONE); 0045 if (!Xcb::Extensions::self()->isCompositeOverlayAvailable()) { 0046 return false; 0047 } 0048 if (!Xcb::Extensions::self()->isShapeInputAvailable()) { // needed in setupOverlay() 0049 return false; 0050 } 0051 #ifdef KWIN_HAVE_XCOMPOSITE_OVERLAY 0052 Xcb::OverlayWindow overlay(rootWindow()); 0053 if (overlay.isNull()) { 0054 return false; 0055 } 0056 m_window = overlay->overlay_win; 0057 if (m_window == XCB_WINDOW_NONE) { 0058 return false; 0059 } 0060 resize(workspace()->geometry().size()); 0061 return true; 0062 #else 0063 return false; 0064 #endif 0065 } 0066 0067 void OverlayWindowX11::setup(xcb_window_t window) 0068 { 0069 Q_ASSERT(m_window != XCB_WINDOW_NONE); 0070 Q_ASSERT(Xcb::Extensions::self()->isShapeInputAvailable()); 0071 setNoneBackgroundPixmap(m_window); 0072 m_shape = QRegion(); 0073 const QSize &s = workspace()->geometry().size(); 0074 setShape(QRect(0, 0, s.width(), s.height())); 0075 if (window != XCB_WINDOW_NONE) { 0076 setNoneBackgroundPixmap(window); 0077 setupInputShape(window); 0078 } 0079 const uint32_t eventMask = XCB_EVENT_MASK_VISIBILITY_CHANGE; 0080 xcb_change_window_attributes(connection(), m_window, XCB_CW_EVENT_MASK, &eventMask); 0081 } 0082 0083 void OverlayWindowX11::setupInputShape(xcb_window_t window) 0084 { 0085 xcb_shape_rectangles(connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT, XCB_CLIP_ORDERING_UNSORTED, window, 0, 0, 0, nullptr); 0086 } 0087 0088 void OverlayWindowX11::setNoneBackgroundPixmap(xcb_window_t window) 0089 { 0090 const uint32_t mask = XCB_BACK_PIXMAP_NONE; 0091 xcb_change_window_attributes(connection(), window, XCB_CW_BACK_PIXMAP, &mask); 0092 } 0093 0094 void OverlayWindowX11::show() 0095 { 0096 Q_ASSERT(m_window != XCB_WINDOW_NONE); 0097 if (m_shown) { 0098 return; 0099 } 0100 xcb_map_subwindows(connection(), m_window); 0101 xcb_map_window(connection(), m_window); 0102 m_shown = true; 0103 } 0104 0105 void OverlayWindowX11::hide() 0106 { 0107 Q_ASSERT(m_window != XCB_WINDOW_NONE); 0108 xcb_unmap_window(connection(), m_window); 0109 m_shown = false; 0110 const QSize &s = workspace()->geometry().size(); 0111 setShape(QRect(0, 0, s.width(), s.height())); 0112 } 0113 0114 void OverlayWindowX11::setShape(const QRegion ®) 0115 { 0116 // Avoid setting the same shape again, it causes flicker (apparently it is not a no-op 0117 // and triggers something). 0118 if (reg == m_shape) { 0119 return; 0120 } 0121 const QVector<xcb_rectangle_t> xrects = Xcb::regionToRects(reg); 0122 xcb_shape_rectangles(connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED, 0123 m_window, 0, 0, xrects.count(), xrects.data()); 0124 setupInputShape(m_window); 0125 m_shape = reg; 0126 } 0127 0128 void OverlayWindowX11::resize(const QSize &size) 0129 { 0130 Q_ASSERT(m_window != XCB_WINDOW_NONE); 0131 const uint32_t geometry[2] = { 0132 static_cast<uint32_t>(size.width()), 0133 static_cast<uint32_t>(size.height())}; 0134 xcb_configure_window(connection(), m_window, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, geometry); 0135 setShape(QRegion(0, 0, size.width(), size.height())); 0136 } 0137 0138 bool OverlayWindowX11::isVisible() const 0139 { 0140 return m_visible; 0141 } 0142 0143 void OverlayWindowX11::setVisibility(bool visible) 0144 { 0145 m_visible = visible; 0146 } 0147 0148 void OverlayWindowX11::destroy() 0149 { 0150 if (m_window == XCB_WINDOW_NONE) { 0151 return; 0152 } 0153 // reset the overlay shape 0154 const QSize &s = workspace()->geometry().size(); 0155 xcb_rectangle_t rec = {0, 0, static_cast<uint16_t>(s.width()), static_cast<uint16_t>(s.height())}; 0156 xcb_shape_rectangles(connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED, m_window, 0, 0, 1, &rec); 0157 xcb_shape_rectangles(connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT, XCB_CLIP_ORDERING_UNSORTED, m_window, 0, 0, 1, &rec); 0158 #ifdef KWIN_HAVE_XCOMPOSITE_OVERLAY 0159 xcb_composite_release_overlay_window(connection(), m_window); 0160 #endif 0161 m_window = XCB_WINDOW_NONE; 0162 m_shown = false; 0163 } 0164 0165 xcb_window_t OverlayWindowX11::window() const 0166 { 0167 return m_window; 0168 } 0169 0170 bool OverlayWindowX11::event(xcb_generic_event_t *event) 0171 { 0172 const uint8_t eventType = event->response_type & ~0x80; 0173 if (eventType == XCB_EXPOSE) { 0174 const auto *expose = reinterpret_cast<xcb_expose_event_t *>(event); 0175 if (expose->window == rootWindow() // root window needs repainting 0176 || (m_window != XCB_WINDOW_NONE && expose->window == m_window)) { // overlay needs repainting 0177 Compositor::self()->scene()->addRepaint(expose->x, expose->y, expose->width, expose->height); 0178 } 0179 } else if (eventType == XCB_VISIBILITY_NOTIFY) { 0180 const auto *visibility = reinterpret_cast<xcb_visibility_notify_event_t *>(event); 0181 if (m_window != XCB_WINDOW_NONE && visibility->window == m_window) { 0182 bool was_visible = isVisible(); 0183 setVisibility((visibility->state != XCB_VISIBILITY_FULLY_OBSCURED)); 0184 auto compositor = Compositor::self(); 0185 if (!was_visible && m_visible) { 0186 // hack for #154825 0187 compositor->scene()->addRepaintFull(); 0188 QTimer::singleShot(2000, compositor, [compositor]() { 0189 if (compositor->compositing()) { 0190 compositor->scene()->addRepaintFull(); 0191 } 0192 }); 0193 } 0194 compositor->scheduleRepaint(); 0195 } 0196 } 0197 return false; 0198 } 0199 0200 } // namespace KWin