File indexing completed on 2024-11-10 04:56:36

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 "compositor.h"
0013 #include "core/renderloop.h"
0014 #include "scene/workspacescene.h"
0015 #include "utils/xcbutils.h"
0016 #include "x11_standalone_backend.h"
0017 
0018 #include <QList>
0019 
0020 #include <xcb/composite.h>
0021 #include <xcb/shape.h>
0022 #if XCB_COMPOSITE_MAJOR_VERSION > 0 || XCB_COMPOSITE_MINOR_VERSION >= 3
0023 #define KWIN_HAVE_XCOMPOSITE_OVERLAY
0024 #endif
0025 
0026 namespace KWin
0027 {
0028 OverlayWindowX11::OverlayWindowX11(X11StandaloneBackend *backend)
0029     : OverlayWindow()
0030     , X11EventFilter(QList<int>{XCB_EXPOSE, XCB_VISIBILITY_NOTIFY})
0031     , m_visible(true)
0032     , m_shown(false)
0033     , m_backend(backend)
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     return true;
0061 #else
0062     return false;
0063 #endif
0064 }
0065 
0066 void OverlayWindowX11::setup(xcb_window_t window)
0067 {
0068     Q_ASSERT(m_window != XCB_WINDOW_NONE);
0069     Q_ASSERT(Xcb::Extensions::self()->isShapeInputAvailable());
0070     setNoneBackgroundPixmap(m_window);
0071     if (m_size.isValid()) {
0072         setShape(QRect(0, 0, m_size.width(), m_size.height()));
0073     }
0074     if (window != XCB_WINDOW_NONE) {
0075         setNoneBackgroundPixmap(window);
0076         setupInputShape(window);
0077     }
0078     const uint32_t eventMask = XCB_EVENT_MASK_VISIBILITY_CHANGE;
0079     xcb_change_window_attributes(connection(), m_window, XCB_CW_EVENT_MASK, &eventMask);
0080 }
0081 
0082 void OverlayWindowX11::setupInputShape(xcb_window_t window)
0083 {
0084     xcb_shape_rectangles(connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT, XCB_CLIP_ORDERING_UNSORTED, window, 0, 0, 0, nullptr);
0085 }
0086 
0087 void OverlayWindowX11::setNoneBackgroundPixmap(xcb_window_t window)
0088 {
0089     const uint32_t mask = XCB_BACK_PIXMAP_NONE;
0090     xcb_change_window_attributes(connection(), window, XCB_CW_BACK_PIXMAP, &mask);
0091 }
0092 
0093 void OverlayWindowX11::show()
0094 {
0095     Q_ASSERT(m_window != XCB_WINDOW_NONE);
0096     if (m_shown) {
0097         return;
0098     }
0099     xcb_map_subwindows(connection(), m_window);
0100     xcb_map_window(connection(), m_window);
0101     m_shown = true;
0102 }
0103 
0104 void OverlayWindowX11::hide()
0105 {
0106     Q_ASSERT(m_window != XCB_WINDOW_NONE);
0107     xcb_unmap_window(connection(), m_window);
0108     m_shown = false;
0109     const QSize &s = m_size;
0110     setShape(QRect(0, 0, s.width(), s.height()));
0111 }
0112 
0113 void OverlayWindowX11::setShape(const QRegion &reg)
0114 {
0115     // Avoid setting the same shape again, it causes flicker (apparently it is not a no-op
0116     // and triggers something).
0117     if (reg == m_shape) {
0118         return;
0119     }
0120     const QList<xcb_rectangle_t> xrects = Xcb::regionToRects(reg);
0121     xcb_shape_rectangles(connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED,
0122                          m_window, 0, 0, xrects.count(), xrects.data());
0123     setupInputShape(m_window);
0124     m_shape = reg;
0125 }
0126 
0127 void OverlayWindowX11::resize(const QSize &size)
0128 {
0129     m_size = size;
0130     if (m_window == XCB_WINDOW_NONE) {
0131         return;
0132     }
0133     const uint32_t geometry[2] = {
0134         static_cast<uint32_t>(size.width()),
0135         static_cast<uint32_t>(size.height())};
0136     xcb_configure_window(connection(), m_window, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, geometry);
0137     setShape(QRegion(0, 0, size.width(), size.height()));
0138 }
0139 
0140 bool OverlayWindowX11::isVisible() const
0141 {
0142     return m_visible;
0143 }
0144 
0145 void OverlayWindowX11::setVisibility(bool visible)
0146 {
0147     m_visible = visible;
0148 }
0149 
0150 void OverlayWindowX11::destroy()
0151 {
0152     if (m_window == XCB_WINDOW_NONE) {
0153         return;
0154     }
0155     // reset the overlay shape
0156     xcb_rectangle_t rec = {0, 0, static_cast<uint16_t>(m_size.width()), static_cast<uint16_t>(m_size.height())};
0157     xcb_shape_rectangles(connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED, m_window, 0, 0, 1, &rec);
0158     xcb_shape_rectangles(connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT, XCB_CLIP_ORDERING_UNSORTED, m_window, 0, 0, 1, &rec);
0159 #ifdef KWIN_HAVE_XCOMPOSITE_OVERLAY
0160     xcb_composite_release_overlay_window(connection(), m_window);
0161 #endif
0162     m_window = XCB_WINDOW_NONE;
0163     m_shown = false;
0164 }
0165 
0166 xcb_window_t OverlayWindowX11::window() const
0167 {
0168     return m_window;
0169 }
0170 
0171 bool OverlayWindowX11::event(xcb_generic_event_t *event)
0172 {
0173     const uint8_t eventType = event->response_type & ~0x80;
0174     if (eventType == XCB_EXPOSE) {
0175         const auto *expose = reinterpret_cast<xcb_expose_event_t *>(event);
0176         if (expose->window == rootWindow() // root window needs repainting
0177             || (m_window != XCB_WINDOW_NONE && expose->window == m_window)) { // overlay needs repainting
0178             Compositor::self()->scene()->addRepaint(expose->x, expose->y, expose->width, expose->height);
0179         }
0180     } else if (eventType == XCB_VISIBILITY_NOTIFY) {
0181         const auto *visibility = reinterpret_cast<xcb_visibility_notify_event_t *>(event);
0182         if (m_window != XCB_WINDOW_NONE && visibility->window == m_window) {
0183             bool was_visible = isVisible();
0184             setVisibility((visibility->state != XCB_VISIBILITY_FULLY_OBSCURED));
0185             auto compositor = Compositor::self();
0186             if (!was_visible && m_visible) {
0187                 // hack for #154825
0188                 compositor->scene()->addRepaintFull();
0189                 QTimer::singleShot(2000, compositor, [compositor]() {
0190                     if (compositor->compositing()) {
0191                         compositor->scene()->addRepaintFull();
0192                     }
0193                 });
0194             }
0195             m_backend->renderLoop()->scheduleRepaint();
0196         }
0197     }
0198     return false;
0199 }
0200 
0201 } // namespace KWin