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 &reg)
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