File indexing completed on 2024-04-28 16:49:02

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "unmanaged.h"
0011 
0012 #include "deleted.h"
0013 #include "effects.h"
0014 #include "scene/surfaceitem_x11.h"
0015 #include "scene/windowitem.h"
0016 #include "utils/common.h"
0017 #include "wayland/surface_interface.h"
0018 #include "workspace.h"
0019 
0020 #include <QDebug>
0021 #include <QTimer>
0022 #include <QWidget>
0023 #include <QWindow>
0024 
0025 #include <xcb/shape.h>
0026 
0027 using namespace KWaylandServer;
0028 
0029 namespace KWin
0030 {
0031 
0032 // window types that are supported as unmanaged (mainly for compositing)
0033 const NET::WindowTypes SUPPORTED_UNMANAGED_WINDOW_TYPES_MASK = NET::NormalMask
0034     | NET::DesktopMask
0035     | NET::DockMask
0036     | NET::ToolbarMask
0037     | NET::MenuMask
0038     | NET::DialogMask
0039     /*| NET::OverrideMask*/
0040     | NET::TopMenuMask
0041     | NET::UtilityMask
0042     | NET::SplashMask
0043     | NET::DropdownMenuMask
0044     | NET::PopupMenuMask
0045     | NET::TooltipMask
0046     | NET::NotificationMask
0047     | NET::ComboBoxMask
0048     | NET::DNDIconMask
0049     | NET::OnScreenDisplayMask
0050     | NET::CriticalNotificationMask;
0051 
0052 Unmanaged::Unmanaged()
0053     : Window()
0054 {
0055     switch (kwinApp()->operationMode()) {
0056     case Application::OperationModeXwayland:
0057         // The wayland surface is associated with the override-redirect window asynchronously.
0058         connect(this, &Window::surfaceChanged, this, &Unmanaged::associate);
0059         break;
0060     case Application::OperationModeX11:
0061         // We have no way knowing whether the override-redirect window can be painted. Mark it
0062         // as ready for painting after synthetic 50ms delay.
0063         QTimer::singleShot(50, this, &Unmanaged::initialize);
0064         break;
0065     case Application::OperationModeWaylandOnly:
0066         Q_UNREACHABLE();
0067     }
0068 }
0069 
0070 Unmanaged::~Unmanaged()
0071 {
0072 }
0073 
0074 std::unique_ptr<WindowItem> Unmanaged::createItem(Scene *scene)
0075 {
0076     return std::make_unique<WindowItemX11>(this, scene);
0077 }
0078 
0079 void Unmanaged::associate()
0080 {
0081     if (surface()->isMapped()) {
0082         initialize();
0083     } else {
0084         // Queued connection because we want to mark the window ready for painting after
0085         // the associated surface item has processed the new surface state.
0086         connect(surface(), &SurfaceInterface::mapped, this, &Unmanaged::initialize, Qt::QueuedConnection);
0087     }
0088 }
0089 
0090 void Unmanaged::initialize()
0091 {
0092     setReadyForPainting();
0093 }
0094 
0095 bool Unmanaged::track(xcb_window_t w)
0096 {
0097     XServerGrabber xserverGrabber;
0098     Xcb::WindowAttributes attr(w);
0099     Xcb::WindowGeometry geo(w);
0100     if (attr.isNull() || attr->map_state != XCB_MAP_STATE_VIEWABLE) {
0101         return false;
0102     }
0103     if (attr->_class == XCB_WINDOW_CLASS_INPUT_ONLY) {
0104         return false;
0105     }
0106     if (geo.isNull()) {
0107         return false;
0108     }
0109 
0110     setWindowHandles(w); // the window is also the frame
0111     Xcb::selectInput(w, attr->your_event_mask | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE);
0112     m_bufferGeometry = geo.rect();
0113     m_frameGeometry = geo.rect();
0114     m_clientGeometry = geo.rect();
0115     checkOutput();
0116     m_visual = attr->visual;
0117     bit_depth = geo->depth;
0118     info = new NETWinInfo(kwinApp()->x11Connection(), w, kwinApp()->x11RootWindow(),
0119                           NET::WMWindowType | NET::WMPid,
0120                           NET::WM2Opacity | NET::WM2WindowRole | NET::WM2WindowClass | NET::WM2OpaqueRegion);
0121     setOpacity(info->opacityF());
0122     getResourceClass();
0123     getWmClientLeader();
0124     getWmClientMachine();
0125     if (Xcb::Extensions::self()->isShapeAvailable()) {
0126         xcb_shape_select_input(kwinApp()->x11Connection(), w, true);
0127     }
0128     detectShape(w);
0129     getWmOpaqueRegion();
0130     getSkipCloseAnimation();
0131     setupCompositing();
0132     if (QWindow *internalWindow = findInternalWindow()) {
0133         m_outline = internalWindow->property("__kwin_outline").toBool();
0134     }
0135     if (effects) {
0136         static_cast<EffectsHandlerImpl *>(effects)->checkInputWindowStacking();
0137     }
0138     return true;
0139 }
0140 
0141 void Unmanaged::release(ReleaseReason releaseReason)
0142 {
0143     Deleted *del = nullptr;
0144     if (releaseReason != ReleaseReason::KWinShutsDown) {
0145         del = Deleted::create(this);
0146     }
0147     Q_EMIT windowClosed(this, del);
0148     finishCompositing(releaseReason);
0149     if (!QWidget::find(window()) && releaseReason != ReleaseReason::Destroyed) { // don't affect our own windows
0150         if (Xcb::Extensions::self()->isShapeAvailable()) {
0151             xcb_shape_select_input(kwinApp()->x11Connection(), window(), false);
0152         }
0153         Xcb::selectInput(window(), XCB_EVENT_MASK_NO_EVENT);
0154     }
0155     workspace()->removeUnmanaged(this);
0156     if (releaseReason != ReleaseReason::KWinShutsDown) {
0157         disownDataPassedToDeleted();
0158         del->unrefWindow();
0159     }
0160     deleteUnmanaged(this);
0161 }
0162 
0163 void Unmanaged::deleteUnmanaged(Unmanaged *c)
0164 {
0165     delete c;
0166 }
0167 
0168 bool Unmanaged::hasScheduledRelease() const
0169 {
0170     return m_scheduledRelease;
0171 }
0172 
0173 int Unmanaged::desktop() const
0174 {
0175     return NET::OnAllDesktops; // TODO for some window types should be the current desktop?
0176 }
0177 
0178 QStringList Unmanaged::activities() const
0179 {
0180     return QStringList();
0181 }
0182 
0183 QVector<VirtualDesktop *> Unmanaged::desktops() const
0184 {
0185     return QVector<VirtualDesktop *>();
0186 }
0187 
0188 QPointF Unmanaged::clientPos() const
0189 {
0190     return QPoint(0, 0); // unmanaged windows don't have decorations
0191 }
0192 
0193 NET::WindowType Unmanaged::windowType(bool direct, int supportedTypes) const
0194 {
0195     // for unmanaged windows the direct does not make any difference
0196     // as there are no rules to check and no hacks to apply
0197     if (supportedTypes == 0) {
0198         supportedTypes = SUPPORTED_UNMANAGED_WINDOW_TYPES_MASK;
0199     }
0200     return info->windowType(NET::WindowTypes(supportedTypes));
0201 }
0202 
0203 bool Unmanaged::isOutline() const
0204 {
0205     return m_outline;
0206 }
0207 
0208 bool Unmanaged::isUnmanaged() const
0209 {
0210     return true;
0211 }
0212 
0213 QWindow *Unmanaged::findInternalWindow() const
0214 {
0215     const QWindowList windows = kwinApp()->topLevelWindows();
0216     for (QWindow *w : windows) {
0217         if (w->handle() && w->winId() == window()) {
0218             return w;
0219         }
0220     }
0221     return nullptr;
0222 }
0223 
0224 void Unmanaged::checkOutput()
0225 {
0226     setOutput(workspace()->outputAt(frameGeometry().center()));
0227 }
0228 
0229 void Unmanaged::damageNotifyEvent()
0230 {
0231     Q_ASSERT(kwinApp()->operationMode() == Application::OperationModeX11);
0232     SurfaceItemX11 *item = static_cast<SurfaceItemX11 *>(surfaceItem());
0233     if (item) {
0234         item->processDamage();
0235     }
0236 }
0237 
0238 } // namespace