diff --git a/autotests/integration/xwaylandserver_crash_test.cpp b/autotests/integration/xwaylandserver_crash_test.cpp index a99f9f0535..f08e13a573 100644 --- a/autotests/integration/xwaylandserver_crash_test.cpp +++ b/autotests/integration/xwaylandserver_crash_test.cpp @@ -11,7 +11,6 @@ #include "core/outputbackend.h" #include "main.h" #include "scene/workspacescene.h" -#include "unmanaged.h" #include "wayland_server.h" #include "workspace.h" #include "x11window.h" @@ -36,7 +35,6 @@ private Q_SLOTS: void XwaylandServerCrashTest::initTestCase() { - qRegisterMetaType(); qRegisterMetaType(); QSignalSpy applicationStartedSpy(kwinApp(), &Application::started); QVERIFY(waylandServer()->init(s_socketName)); @@ -99,7 +97,7 @@ void XwaylandServerCrashTest::testCrash() QSignalSpy unmanagedAddedSpy(workspace(), &Workspace::windowAdded); QVERIFY(unmanagedAddedSpy.wait()); - QPointer unmanaged = unmanagedAddedSpy.last().first().value(); + QPointer unmanaged = unmanagedAddedSpy.last().first().value(); QVERIFY(unmanaged); // Let's pretend that the Xwayland process has crashed. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f008c4a4b2..3e2d8b1b21 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -163,7 +163,6 @@ target_sources(kwin PRIVATE tiles/tile.cpp tiles/tilemanager.cpp touch_input.cpp - unmanaged.cpp useractions.cpp virtualdesktops.cpp virtualdesktopsdbustypes.cpp diff --git a/src/backends/x11/standalone/x11_standalone_windowselector.cpp b/src/backends/x11/standalone/x11_standalone_windowselector.cpp index 128df24ea2..af3aa00db9 100644 --- a/src/backends/x11/standalone/x11_standalone_windowselector.cpp +++ b/src/backends/x11/standalone/x11_standalone_windowselector.cpp @@ -10,7 +10,6 @@ */ #include "x11_standalone_windowselector.h" #include "cursor.h" -#include "unmanaged.h" #include "utils/xcbutils.h" #include "workspace.h" #include "x11window.h" diff --git a/src/composite.cpp b/src/composite.cpp index faca40aeff..cddc611050 100644 --- a/src/composite.cpp +++ b/src/composite.cpp @@ -32,7 +32,6 @@ #include "scene/workspacescene_opengl.h" #include "scene/workspacescene_qpainter.h" #include "shadow.h" -#include "unmanaged.h" #include "useractions.h" #include "utils/common.h" #include "utils/xcbutils.h" diff --git a/src/dbusinterface.cpp b/src/dbusinterface.cpp index 5197c0f84f..a794534703 100644 --- a/src/dbusinterface.cpp +++ b/src/dbusinterface.cpp @@ -23,7 +23,6 @@ #include "main.h" #include "placement.h" #include "pluginmanager.h" -#include "unmanaged.h" #include "virtualdesktops.h" #include "window.h" #include "workspace.h" diff --git a/src/debug_console.cpp b/src/debug_console.cpp index 7b4056d6c9..e273963ec8 100644 --- a/src/debug_console.cpp +++ b/src/debug_console.cpp @@ -16,7 +16,6 @@ #include "libkwineffects/kwinglutils.h" #include "main.h" #include "platformsupport/scenes/opengl/openglbackend.h" -#include "unmanaged.h" #include "utils/filedescriptor.h" #include "utils/subsurfacemonitor.h" #include "wayland/abstract_data_source.h" @@ -926,7 +925,11 @@ DebugConsoleModel::DebugConsoleModel(QObject *parent) void DebugConsoleModel::handleWindowAdded(Window *window) { if (auto x11 = qobject_cast(window)) { - add(s_x11WindowId - 1, m_x11Windows, x11); + if (x11->isUnmanaged()) { + add(s_x11UnmanagedId - 1, m_unmanageds, x11); + } else { + add(s_x11WindowId - 1, m_x11Windows, x11); + } return; } @@ -939,17 +942,16 @@ void DebugConsoleModel::handleWindowAdded(Window *window) add(s_workspaceInternalId - 1, m_internalWindows, internal); return; } - - if (auto unmanaged = qobject_cast(window)) { - add(s_x11UnmanagedId - 1, m_unmanageds, unmanaged); - return; - } } void DebugConsoleModel::handleWindowRemoved(Window *window) { if (auto x11 = qobject_cast(window)) { - remove(s_x11WindowId - 1, m_x11Windows, x11); + if (x11->isUnmanaged()) { + remove(s_x11UnmanagedId - 1, m_unmanageds, x11); + } else { + remove(s_x11WindowId - 1, m_x11Windows, x11); + } return; } @@ -962,11 +964,6 @@ void DebugConsoleModel::handleWindowRemoved(Window *window) remove(s_workspaceInternalId - 1, m_internalWindows, internal); return; } - - if (auto unmanaged = qobject_cast(window)) { - remove(s_x11UnmanagedId - 1, m_unmanageds, unmanaged); - return; - } } DebugConsoleModel::~DebugConsoleModel() = default; @@ -1230,7 +1227,7 @@ QVariant DebugConsoleModel::data(const QModelIndex &index, int role) const return propertyData(w, index, role); } else if (X11Window *w = x11Window(index)) { return propertyData(w, index, role); - } else if (Unmanaged *u = unmanaged(index)) { + } else if (X11Window *u = unmanaged(index)) { return propertyData(u, index, role); } } else { @@ -1293,7 +1290,7 @@ X11Window *DebugConsoleModel::x11Window(const QModelIndex &index) const return windowForIndex(index, m_x11Windows, s_x11WindowId); } -Unmanaged *DebugConsoleModel::unmanaged(const QModelIndex &index) const +X11Window *DebugConsoleModel::unmanaged(const QModelIndex &index) const { return windowForIndex(index, m_unmanageds, s_x11UnmanagedId); } diff --git a/src/debug_console.h b/src/debug_console.h index b26a2706d0..265db4c5d0 100644 --- a/src/debug_console.h +++ b/src/debug_console.h @@ -37,7 +37,6 @@ namespace KWin class Window; class X11Window; class InternalWindow; -class Unmanaged; class DebugConsoleFilter; class WaylandWindow; @@ -75,13 +74,13 @@ private: WaylandWindow *waylandWindow(const QModelIndex &index) const; InternalWindow *internalWindow(const QModelIndex &index) const; X11Window *x11Window(const QModelIndex &index) const; - Unmanaged *unmanaged(const QModelIndex &index) const; + X11Window *unmanaged(const QModelIndex &index) const; int topLevelRowCount() const; QVector m_waylandWindows; QVector m_internalWindows; QVector m_x11Windows; - QVector m_unmanageds; + QVector m_unmanageds; }; class DebugConsoleDelegate : public QStyledItemDelegate diff --git a/src/effects.cpp b/src/effects.cpp index ffab48e3c7..fb0bd117fb 100644 --- a/src/effects.cpp +++ b/src/effects.cpp @@ -28,7 +28,6 @@ #include "pointer_input.h" #include "scene/itemrenderer.h" #include "scripting/scripting.h" -#include "unmanaged.h" #include "x11window.h" #if KWIN_BUILD_TABBOX #include "tabbox/tabbox.h" @@ -996,7 +995,7 @@ EffectWindow *EffectsHandlerImpl::findWindow(WId id) const if (X11Window *w = Workspace::self()->findClient(Predicate::WindowMatch, id)) { return w->effectWindow(); } - if (Unmanaged *w = Workspace::self()->findUnmanaged(id)) { + if (X11Window *w = Workspace::self()->findUnmanaged(id)) { return w->effectWindow(); } return nullptr; @@ -1855,7 +1854,7 @@ EffectWindowImpl::EffectWindowImpl(Window *window) managed = window->isClient(); m_waylandWindow = qobject_cast(window) != nullptr; - m_x11Window = qobject_cast(window) != nullptr || qobject_cast(window) != nullptr; + m_x11Window = qobject_cast(window) != nullptr; } EffectWindowImpl::~EffectWindowImpl() diff --git a/src/events.cpp b/src/events.cpp index cfbc444fd7..4004ab119a 100644 --- a/src/events.cpp +++ b/src/events.cpp @@ -27,7 +27,6 @@ #include "group.h" #include "rules.h" #include "screenedge.h" -#include "unmanaged.h" #include "useractions.h" #include "utils/xcbutils.h" #include "wayland/surface_interface.h" @@ -168,7 +167,7 @@ bool Workspace::workspaceEvent(xcb_generic_event_t *e) if (window->windowEvent(e)) { return true; } - } else if (Unmanaged *window = findUnmanaged(eventWindow)) { + } else if (X11Window *window = findUnmanaged(eventWindow)) { if (window->windowEvent(e)) { return true; } @@ -223,7 +222,7 @@ bool Workspace::workspaceEvent(xcb_generic_event_t *e) case XCB_MAP_NOTIFY: { const auto *event = reinterpret_cast(e); if (event->override_redirect) { - Unmanaged *window = findUnmanaged(event->window); + X11Window *window = findUnmanaged(event->window); if (window == nullptr) { window = createUnmanaged(event->window); } @@ -232,7 +231,7 @@ bool Workspace::workspaceEvent(xcb_generic_event_t *e) // since release is scheduled after map notify, this old Unmanaged will get released // before KWIN has chance to remanage it again. so release it right now. if (window->hasScheduledRelease()) { - window->release(); + window->releaseWindow(); window = createUnmanaged(event->window); } if (window) { @@ -317,6 +316,73 @@ bool Workspace::workspaceEvent(xcb_generic_event_t *e) */ bool X11Window::windowEvent(xcb_generic_event_t *e) { + if (isUnmanaged()) { + NET::Properties dirtyProperties; + NET::Properties2 dirtyProperties2; + info->event(e, &dirtyProperties, &dirtyProperties2); // pass through the NET stuff + if (dirtyProperties2 & NET::WM2Opacity) { + if (Compositor::compositing()) { + setOpacity(info->opacityF()); + } + } + if (dirtyProperties2 & NET::WM2OpaqueRegion) { + getWmOpaqueRegion(); + } + if (dirtyProperties2.testFlag(NET::WM2WindowRole)) { + Q_EMIT windowRoleChanged(); + } + if (dirtyProperties2.testFlag(NET::WM2WindowClass)) { + getResourceClass(); + } + const uint8_t eventType = e->response_type & ~0x80; + switch (eventType) { + case XCB_DESTROY_NOTIFY: + destroyWindow(); + break; + case XCB_UNMAP_NOTIFY: { + workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event + + // unmap notify might have been emitted due to a destroy notify + // but unmap notify gets emitted before the destroy notify, nevertheless at this + // point the window is already destroyed. This means any XCB request with the window + // will cause an error. + // To not run into these errors we try to wait for the destroy notify. For this we + // generate a round trip to the X server and wait a very short time span before + // handling the release. + kwinApp()->updateXTime(); + // using 1 msec to not just move it at the end of the event loop but add an very short + // timespan to cover cases like unmap() followed by destroy(). The only other way to + // ensure that the window is not destroyed when we do the release handling is to grab + // the XServer which we do not want to do for an Unmanaged. The timespan of 1 msec is + // short enough to not cause problems in the close window animations. + // It's of course still possible that we miss the destroy in which case non-fatal + // X errors are reported to the event loop and logged by Qt. + m_releaseTimer.start(1); + break; + } + case XCB_CONFIGURE_NOTIFY: + configureNotifyEvent(reinterpret_cast(e)); + break; + case XCB_PROPERTY_NOTIFY: + propertyNotifyEvent(reinterpret_cast(e)); + break; + case XCB_CLIENT_MESSAGE: + clientMessageEvent(reinterpret_cast(e)); + break; + default: { + if (eventType == Xcb::Extensions::self()->shapeNotifyEvent()) { + detectShape(window()); + Q_EMIT geometryShapeChanged(frameGeometry()); + } + if (eventType == Xcb::Extensions::self()->damageNotifyEvent()) { + damageNotifyEvent(); + } + break; + } + } + return false; // don't eat events, even our own unmanaged widgets are tracked + } + if (findEventWindow(e) == window()) { // avoid doing stuff on frame or wrapper NET::Properties dirtyProperties; NET::Properties2 dirtyProperties2; @@ -596,6 +662,27 @@ void X11Window::clientMessageEvent(xcb_client_message_event_t *e) } } +void X11Window::configureNotifyEvent(xcb_configure_notify_event_t *e) +{ + if (effects) { + static_cast(effects)->checkInputWindowStacking(); // keep them on top + } + QRectF newgeom(Xcb::fromXNative(e->x), Xcb::fromXNative(e->y), Xcb::fromXNative(e->width), Xcb::fromXNative(e->height)); + if (newgeom != m_frameGeometry) { + Q_EMIT frameGeometryAboutToChange(); + + QRectF old = m_frameGeometry; + m_clientGeometry = newgeom; + m_frameGeometry = newgeom; + m_bufferGeometry = newgeom; + checkOutput(); + Q_EMIT bufferGeometryChanged(old); + Q_EMIT clientGeometryChanged(old); + Q_EMIT frameGeometryChanged(old); + Q_EMIT geometryShapeChanged(old); + } +} + /** * Handles configure requests of the client window */ @@ -1210,99 +1297,6 @@ void X11Window::keyPressEvent(uint key_code, xcb_timestamp_t time) Window::keyPressEvent(key_code); } -// **************************************** -// Unmanaged -// **************************************** - -bool Unmanaged::windowEvent(xcb_generic_event_t *e) -{ - NET::Properties dirtyProperties; - NET::Properties2 dirtyProperties2; - info->event(e, &dirtyProperties, &dirtyProperties2); // pass through the NET stuff - if (dirtyProperties2 & NET::WM2Opacity) { - if (Compositor::compositing()) { - setOpacity(info->opacityF()); - } - } - if (dirtyProperties2 & NET::WM2OpaqueRegion) { - getWmOpaqueRegion(); - } - if (dirtyProperties2.testFlag(NET::WM2WindowRole)) { - Q_EMIT windowRoleChanged(); - } - if (dirtyProperties2.testFlag(NET::WM2WindowClass)) { - getResourceClass(); - } - const uint8_t eventType = e->response_type & ~0x80; - switch (eventType) { - case XCB_DESTROY_NOTIFY: - release(ReleaseReason::Destroyed); - break; - case XCB_UNMAP_NOTIFY: { - workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event - - // unmap notify might have been emitted due to a destroy notify - // but unmap notify gets emitted before the destroy notify, nevertheless at this - // point the window is already destroyed. This means any XCB request with the window - // will cause an error. - // To not run into these errors we try to wait for the destroy notify. For this we - // generate a round trip to the X server and wait a very short time span before - // handling the release. - kwinApp()->updateXTime(); - // using 1 msec to not just move it at the end of the event loop but add an very short - // timespan to cover cases like unmap() followed by destroy(). The only other way to - // ensure that the window is not destroyed when we do the release handling is to grab - // the XServer which we do not want to do for an Unmanaged. The timespan of 1 msec is - // short enough to not cause problems in the close window animations. - // It's of course still possible that we miss the destroy in which case non-fatal - // X errors are reported to the event loop and logged by Qt. - m_releaseTimer.start(1); - break; - } - case XCB_CONFIGURE_NOTIFY: - configureNotifyEvent(reinterpret_cast(e)); - break; - case XCB_PROPERTY_NOTIFY: - propertyNotifyEvent(reinterpret_cast(e)); - break; - case XCB_CLIENT_MESSAGE: - clientMessageEvent(reinterpret_cast(e)); - break; - default: { - if (eventType == Xcb::Extensions::self()->shapeNotifyEvent()) { - detectShape(window()); - Q_EMIT geometryShapeChanged(frameGeometry()); - } - if (eventType == Xcb::Extensions::self()->damageNotifyEvent()) { - damageNotifyEvent(); - } - break; - } - } - return false; // don't eat events, even our own unmanaged widgets are tracked -} - -void Unmanaged::configureNotifyEvent(xcb_configure_notify_event_t *e) -{ - if (effects) { - static_cast(effects)->checkInputWindowStacking(); // keep them on top - } - QRectF newgeom(Xcb::fromXNative(e->x), Xcb::fromXNative(e->y), Xcb::fromXNative(e->width), Xcb::fromXNative(e->height)); - if (newgeom != m_frameGeometry) { - Q_EMIT frameGeometryAboutToChange(); - - QRectF old = m_frameGeometry; - m_clientGeometry = newgeom; - m_frameGeometry = newgeom; - m_bufferGeometry = newgeom; - checkOutput(); - Q_EMIT bufferGeometryChanged(old); - Q_EMIT clientGeometryChanged(old); - Q_EMIT frameGeometryChanged(old); - Q_EMIT geometryShapeChanged(old); - } -} - // **************************************** // Window // **************************************** diff --git a/src/input.cpp b/src/input.cpp index f10d336a9d..6d54622196 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -40,7 +40,6 @@ #include "internalwindow.h" #include "popup_input_filter.h" #include "screenedge.h" -#include "unmanaged.h" #include "virtualdesktops.h" #include "wayland/display.h" #include "wayland/inputmethod_v1_interface.h" diff --git a/src/killwindow.cpp b/src/killwindow.cpp index 6ec0ca4011..9e94ea06b0 100644 --- a/src/killwindow.cpp +++ b/src/killwindow.cpp @@ -11,7 +11,6 @@ #include "killwindow.h" #include "main.h" #include "osd.h" -#include "unmanaged.h" #include "window.h" #include diff --git a/src/layers.cpp b/src/layers.cpp index b9c370d6ba..efa4391356 100644 --- a/src/layers.cpp +++ b/src/layers.cpp @@ -73,7 +73,6 @@ #include "rules.h" #include "screenedge.h" #include "tabbox/tabbox.h" -#include "unmanaged.h" #include "utils/common.h" #include "virtualdesktops.h" #include "wayland_server.h" @@ -162,7 +161,7 @@ void Workspace::propagateWindows(bool propagate_new_windows) for (int i = stacking_order.size() - 1; i >= 0; --i) { X11Window *window = qobject_cast(stacking_order.at(i)); - if (!window || window->hiddenPreview()) { + if (!window || window->isUnmanaged() || window->hiddenPreview()) { continue; } @@ -179,7 +178,7 @@ void Workspace::propagateWindows(bool propagate_new_windows) // these windows that should be unmapped to interfere with other windows for (int i = stacking_order.size() - 1; i >= 0; --i) { X11Window *window = qobject_cast(stacking_order.at(i)); - if (!window || !window->hiddenPreview()) { + if (!window || window->isUnmanaged() || !window->hiddenPreview()) { continue; } newWindowStack << window->frameId(); @@ -197,7 +196,7 @@ void Workspace::propagateWindows(bool propagate_new_windows) } for (Window *window : std::as_const(m_windows)) { X11Window *x11Window = qobject_cast(window); - if (x11Window) { + if (x11Window && !x11Window->isUnmanaged()) { cl.push_back(x11Window->window()); } } @@ -207,7 +206,7 @@ void Workspace::propagateWindows(bool propagate_new_windows) cl.clear(); for (auto it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) { X11Window *window = qobject_cast(*it); - if (window) { + if (window && !window->isUnmanaged()) { cl.push_back(window->window()); } } @@ -467,7 +466,7 @@ void Workspace::restoreSessionStackingOrder(X11Window *window) unconstrained_stacking_order.removeAll(window); for (auto it = unconstrained_stacking_order.begin(); it != unconstrained_stacking_order.end(); ++it) { X11Window *current = qobject_cast(*it); - if (!current) { + if (!current || current->isUnmanaged()) { continue; } if (current->sessionStackingOrder() > window->sessionStackingOrder()) { diff --git a/src/scene/workspacescene.cpp b/src/scene/workspacescene.cpp index bdc9b23eff..b9de952a2c 100644 --- a/src/scene/workspacescene.cpp +++ b/src/scene/workspacescene.cpp @@ -66,7 +66,6 @@ #include "scene/surfaceitem.h" #include "scene/windowitem.h" #include "shadow.h" -#include "unmanaged.h" #include "wayland/seat_interface.h" #include "wayland/surface_interface.h" #include "wayland_server.h" diff --git a/src/sm.cpp b/src/sm.cpp index ec73ddf032..de049d4d69 100644 --- a/src/sm.cpp +++ b/src/sm.cpp @@ -91,7 +91,7 @@ void SessionManager::storeSession(const QString &sessionName, SMSavePhase phase) const QList windows = workspace()->windows(); for (auto it = windows.begin(); it != windows.end(); ++it) { X11Window *c = qobject_cast(*it); - if (!c) { + if (!c || c->isUnmanaged()) { continue; } if (c->windowType() > NET::Splash) { @@ -180,7 +180,7 @@ void SessionManager::storeSubSession(const QString &name, QSet sessi for (auto it = windows.begin(); it != windows.end(); ++it) { X11Window *c = qobject_cast(*it); - if (!c) { + if (!c || c->isUnmanaged()) { continue; } if (c->windowType() > NET::Splash) { diff --git a/src/tabbox/tabbox.cpp b/src/tabbox/tabbox.cpp index b2a7922ba6..010750df00 100644 --- a/src/tabbox/tabbox.cpp +++ b/src/tabbox/tabbox.cpp @@ -28,7 +28,6 @@ #include "keyboard_input.h" #include "pointer_input.h" #include "screenedge.h" -#include "unmanaged.h" #include "utils/xcbutils.h" #include "virtualdesktops.h" #include "workspace.h" diff --git a/src/unmanaged.cpp b/src/unmanaged.cpp deleted file mode 100644 index aabd8012f1..0000000000 --- a/src/unmanaged.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2006 Lubos Lunak - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -#include "unmanaged.h" - -#include "effects.h" -#include "scene/surfaceitem_x11.h" -#include "scene/windowitem.h" -#include "utils/common.h" -#include "wayland/surface_interface.h" -#include "workspace.h" - -#include -#include -#include - -#include - -using namespace KWaylandServer; - -namespace KWin -{ - -// window types that are supported as unmanaged (mainly for compositing) -const NET::WindowTypes SUPPORTED_UNMANAGED_WINDOW_TYPES_MASK = NET::NormalMask - | NET::DesktopMask - | NET::DockMask - | NET::ToolbarMask - | NET::MenuMask - | NET::DialogMask - /*| NET::OverrideMask*/ - | NET::TopMenuMask - | NET::UtilityMask - | NET::SplashMask - | NET::DropdownMenuMask - | NET::PopupMenuMask - | NET::TooltipMask - | NET::NotificationMask - | NET::ComboBoxMask - | NET::DNDIconMask - | NET::OnScreenDisplayMask - | NET::CriticalNotificationMask; - -Unmanaged::Unmanaged() - : Window() -{ - m_releaseTimer.setSingleShot(true); - connect(&m_releaseTimer, &QTimer::timeout, this, [this]() { - release(); - }); - - switch (kwinApp()->operationMode()) { - case Application::OperationModeXwayland: - // The wayland surface is associated with the override-redirect window asynchronously. - connect(this, &Window::surfaceChanged, this, &Unmanaged::associate); - break; - case Application::OperationModeX11: - // We have no way knowing whether the override-redirect window can be painted. Mark it - // as ready for painting after synthetic 50ms delay. - QTimer::singleShot(50, this, &Unmanaged::initialize); - break; - case Application::OperationModeWaylandOnly: - Q_UNREACHABLE(); - } -} - -Unmanaged::~Unmanaged() -{ -} - -std::unique_ptr Unmanaged::createItem(Scene *scene) -{ - return std::make_unique(this, scene); -} - -void Unmanaged::associate() -{ - if (surface()->isMapped()) { - initialize(); - } else { - // Queued connection because we want to mark the window ready for painting after - // the associated surface item has processed the new surface state. - connect(surface(), &SurfaceInterface::mapped, this, &Unmanaged::initialize, Qt::QueuedConnection); - } -} - -void Unmanaged::initialize() -{ - setReadyForPainting(); -} - -bool Unmanaged::track(xcb_window_t w) -{ - XServerGrabber xserverGrabber; - Xcb::WindowAttributes attr(w); - Xcb::WindowGeometry geo(w); - if (attr.isNull() || attr->map_state != XCB_MAP_STATE_VIEWABLE) { - return false; - } - if (attr->_class == XCB_WINDOW_CLASS_INPUT_ONLY) { - return false; - } - if (geo.isNull()) { - return false; - } - - setWindowHandles(w); // the window is also the frame - Xcb::selectInput(w, attr->your_event_mask | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE); - m_bufferGeometry = geo.rect(); - m_frameGeometry = geo.rect(); - m_clientGeometry = geo.rect(); - checkOutput(); - m_visual = attr->visual; - bit_depth = geo->depth; - info = new NETWinInfo(kwinApp()->x11Connection(), w, kwinApp()->x11RootWindow(), - NET::WMWindowType | NET::WMPid, - NET::WM2Opacity | NET::WM2WindowRole | NET::WM2WindowClass | NET::WM2OpaqueRegion); - setOpacity(info->opacityF()); - getResourceClass(); - getWmClientLeader(); - getWmClientMachine(); - if (Xcb::Extensions::self()->isShapeAvailable()) { - xcb_shape_select_input(kwinApp()->x11Connection(), w, true); - } - detectShape(w); - getWmOpaqueRegion(); - getSkipCloseAnimation(); - setupCompositing(); - if (QWindow *internalWindow = findInternalWindow()) { - m_outline = internalWindow->property("__kwin_outline").toBool(); - } - if (effects) { - static_cast(effects)->checkInputWindowStacking(); - } - return true; -} - -void Unmanaged::release(ReleaseReason releaseReason) -{ - if (SurfaceItemX11 *item = qobject_cast(surfaceItem())) { - if (releaseReason == ReleaseReason::Destroyed) { - item->forgetDamage(); - } else { - item->destroyDamage(); - } - } - markAsDeleted(); - m_releaseTimer.stop(); - Q_EMIT closed(); - if (releaseReason != ReleaseReason::Destroyed && !findInternalWindow()) { // don't affect our own windows - if (Xcb::Extensions::self()->isShapeAvailable()) { - xcb_shape_select_input(kwinApp()->x11Connection(), window(), false); - } - Xcb::selectInput(window(), XCB_EVENT_MASK_NO_EVENT); - } - workspace()->removeUnmanaged(this); - unref(); -} - -void Unmanaged::deleteUnmanaged(Unmanaged *c) -{ - delete c; -} - -bool Unmanaged::hasScheduledRelease() const -{ - return m_releaseTimer.isActive(); -} - -QStringList Unmanaged::activities() const -{ - return QStringList(); -} - -NET::WindowType Unmanaged::windowType(bool direct) const -{ - return info->windowType(SUPPORTED_UNMANAGED_WINDOW_TYPES_MASK); -} - -bool Unmanaged::isOutline() const -{ - return m_outline; -} - -bool Unmanaged::isUnmanaged() const -{ - return true; -} - -QWindow *Unmanaged::findInternalWindow() const -{ - const QWindowList windows = kwinApp()->topLevelWindows(); - for (QWindow *w : windows) { - if (w->handle() && w->winId() == window()) { - return w; - } - } - return nullptr; -} - -void Unmanaged::checkOutput() -{ - setOutput(workspace()->outputAt(frameGeometry().center())); -} - -void Unmanaged::damageNotifyEvent() -{ - Q_ASSERT(kwinApp()->operationMode() == Application::OperationModeX11); - SurfaceItemX11 *item = static_cast(surfaceItem()); - if (item) { - item->processDamage(); - } -} - -void Unmanaged::killWindow() -{ - xcb_kill_client(kwinApp()->x11Connection(), window()); -} - -} // namespace diff --git a/src/unmanaged.h b/src/unmanaged.h deleted file mode 100644 index 687b945e73..0000000000 --- a/src/unmanaged.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2006 Lubos Lunak - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -#pragma once - -#include - -#include "window.h" - -#include - -namespace KWin -{ - -class KWIN_EXPORT Unmanaged : public Window -{ - Q_OBJECT -public: - explicit Unmanaged(); - bool windowEvent(xcb_generic_event_t *e); - bool track(xcb_window_t w); - bool hasScheduledRelease() const; - static void deleteUnmanaged(Unmanaged *c); - QStringList activities() const override; - NET::WindowType windowType(bool direct = false) const override; - bool isOutline() const override; - bool isUnmanaged() const override; - - QString captionNormal() const override { return {}; } - QString captionSuffix() const override { return {}; } - bool isCloseable() const override { return false; } - bool isShown() const override { return false; } - bool isHiddenInternal() const override { return false; } - void hideClient() override { /* nothing to do */ } - void showClient() override { /* nothing to do */ } - Window *findModal(bool /*allow_itself*/) override { return nullptr; } - bool isResizable() const override { return false; } - bool isMovable() const override { return false; } - bool isMovableAcrossScreens() const override { return false; } - bool takeFocus() override { return false; } - bool wantsInput() const override { return false; } - void killWindow() override; - void destroyWindow() override { /* nothing to do */ } - void closeWindow() override { /* nothing to do */ } - bool acceptsFocus() const override { return false; } - bool belongsToSameApplication(const Window *other, SameApplicationChecks /*checks*/) const override { return other == this; } - void moveResizeInternal(const QRectF & /*rect*/, KWin::Window::MoveResizeMode /*mode*/) override - { /* nothing to do */ - } - void updateCaption() override { /* nothing to do */ } - QRectF resizeWithChecks(const QRectF &geometry, const QSizeF &) override - { /* nothing to do */ - return geometry; - } - std::unique_ptr createItem(Scene *scene) override; - -public Q_SLOTS: - void release(ReleaseReason releaseReason = ReleaseReason::Release); - -private: - ~Unmanaged() override; // use release() - // handlers for X11 events - void configureNotifyEvent(xcb_configure_notify_event_t *e); - void damageNotifyEvent(); - QWindow *findInternalWindow() const; - void checkOutput(); - void associate(); - void initialize(); - bool m_outline = false; - QTimer m_releaseTimer; -}; - -} // namespace diff --git a/src/wayland_server.cpp b/src/wayland_server.cpp index 654dfd3463..9c28fdecb1 100644 --- a/src/wayland_server.cpp +++ b/src/wayland_server.cpp @@ -20,7 +20,6 @@ #include "layershellv1integration.h" #include "main.h" #include "scene/workspacescene.h" -#include "unmanaged.h" #include "utils/serviceutils.h" #include "virtualdesktops.h" #include "wayland/appmenu_interface.h" @@ -357,7 +356,7 @@ bool WaylandServer::init(InitializationFlags flags) return; } - Unmanaged *unmanaged = ws->findUnmanaged([surface](const Unmanaged *unmanaged) { + X11Window *unmanaged = ws->findUnmanaged([surface](const X11Window *unmanaged) { return unmanaged->pendingSurfaceId() == surface->id(); }); if (unmanaged) { @@ -378,7 +377,7 @@ bool WaylandServer::init(InitializationFlags flags) return; } - Unmanaged *unmanaged = workspace()->findUnmanaged([&surface](const Unmanaged *window) { + X11Window *unmanaged = workspace()->findUnmanaged([&surface](const X11Window *window) { return window->surfaceSerial() == surface->serial(); }); if (unmanaged) { diff --git a/src/window_property_notify_x11_filter.cpp b/src/window_property_notify_x11_filter.cpp index 2a6ac9f97d..73754e86cb 100644 --- a/src/window_property_notify_x11_filter.cpp +++ b/src/window_property_notify_x11_filter.cpp @@ -8,7 +8,6 @@ */ #include "window_property_notify_x11_filter.h" #include "effects.h" -#include "unmanaged.h" #include "workspace.h" #include "x11window.h" diff --git a/src/workspace.cpp b/src/workspace.cpp index a557fab8cf..3c02e13b7f 100644 --- a/src/workspace.cpp +++ b/src/workspace.cpp @@ -52,7 +52,6 @@ #include "placeholderoutput.h" #include "placementtracker.h" #include "tiles/tilemanager.h" -#include "unmanaged.h" #include "useractions.h" #include "utils/xcbutils.h" #include "virtualdesktops.h" @@ -440,12 +439,8 @@ void Workspace::cleanupX11() for (Window *window : stack) { if (auto x11 = qobject_cast(window)) { x11->releaseWindow(true); - } else if (auto unmanaged = qobject_cast(window)) { - unmanaged->release(ReleaseReason::KWinShutsDown); - } else { - continue; + removeFromStack(window); } - removeFromStack(window); } manual_overlays.clear(); @@ -677,16 +672,16 @@ X11Window *Workspace::createX11Window(xcb_window_t windowId, bool is_mapped) return window; } -Unmanaged *Workspace::createUnmanaged(xcb_window_t windowId) +X11Window *Workspace::createUnmanaged(xcb_window_t windowId) { if (X11Compositor *compositor = X11Compositor::self()) { if (compositor->checkForOverlayWindow(windowId)) { return nullptr; } } - Unmanaged *window = new Unmanaged(); + X11Window *window = new X11Window(); if (!window->track(windowId)) { - Unmanaged::deleteUnmanaged(window); + X11Window::deleteClient(window); return nullptr; } addUnmanaged(window); @@ -727,7 +722,7 @@ void Workspace::addX11Window(X11Window *window) updateTabbox(); } -void Workspace::addUnmanaged(Unmanaged *window) +void Workspace::addUnmanaged(X11Window *window) { m_windows.append(window); addToStack(window); @@ -748,7 +743,7 @@ void Workspace::removeX11Window(X11Window *window) removeWindow(window); } -void Workspace::removeUnmanaged(Unmanaged *window) +void Workspace::removeUnmanaged(X11Window *window) { Q_ASSERT(m_windows.contains(window)); m_windows.removeOne(window); @@ -1894,7 +1889,7 @@ void Workspace::forEachClient(std::function func) { for (Window *window : std::as_const(m_windows)) { X11Window *x11Window = qobject_cast(window); - if (x11Window) { + if (x11Window && !x11Window->isUnmanaged()) { func(x11Window); } } @@ -1904,27 +1899,27 @@ X11Window *Workspace::findClient(std::function func) co { for (Window *window : std::as_const(m_windows)) { X11Window *x11Window = qobject_cast(window); - if (x11Window && func(x11Window)) { + if (x11Window && !x11Window->isUnmanaged() && func(x11Window)) { return x11Window; } } return nullptr; } -Unmanaged *Workspace::findUnmanaged(std::function func) const +X11Window *Workspace::findUnmanaged(std::function func) const { for (Window *window : m_windows) { - Unmanaged *unmanaged = qobject_cast(window); - if (unmanaged && func(unmanaged)) { - return unmanaged; + X11Window *x11Window = qobject_cast(window); + if (x11Window && x11Window->isUnmanaged() && func(x11Window)) { + return x11Window; } } return nullptr; } -Unmanaged *Workspace::findUnmanaged(xcb_window_t w) const +X11Window *Workspace::findUnmanaged(xcb_window_t w) const { - return findUnmanaged([w](const Unmanaged *u) { + return findUnmanaged([w](const X11Window *u) { return u->window() == w; }); } diff --git a/src/workspace.h b/src/workspace.h index c3f2562c8e..96edd7f526 100644 --- a/src/workspace.h +++ b/src/workspace.h @@ -58,7 +58,6 @@ class Group; class InternalWindow; class KillWindow; class ShortcutDialog; -class Unmanaged; class UserActionsMenu; class VirtualDesktop; class X11Window; @@ -135,14 +134,14 @@ public: */ X11Window *findClient(Predicate predicate, xcb_window_t w) const; void forEachClient(std::function func); - Unmanaged *findUnmanaged(std::function func) const; + X11Window *findUnmanaged(std::function func) const; /** * @brief Finds the Unmanaged with the given window id. * * @param w The window id to search for * @return KWin::Unmanaged* Found Unmanaged or @c null if there is no Unmanaged with given Id. */ - Unmanaged *findUnmanaged(xcb_window_t w) const; + X11Window *findUnmanaged(xcb_window_t w) const; Window *findWindow(const QUuid &internalId) const; Window *findWindow(std::function func) const; @@ -353,7 +352,7 @@ public: Group *findClientLeaderGroup(const X11Window *c) const; int unconstainedStackingOrderIndex(const X11Window *c) const; - void removeUnmanaged(Unmanaged *); // Only called from Unmanaged::release() + void removeUnmanaged(X11Window *); void removeDeleted(Window *); void addDeleted(Window *); @@ -592,8 +591,8 @@ private: X11Window *createX11Window(xcb_window_t windowId, bool is_mapped); void addX11Window(X11Window *c); void setupWindowConnections(Window *window); - Unmanaged *createUnmanaged(xcb_window_t windowId); - void addUnmanaged(Unmanaged *c); + X11Window *createUnmanaged(xcb_window_t windowId); + void addUnmanaged(X11Window *c); void addWaylandWindow(Window *window); void removeWaylandWindow(Window *window); diff --git a/src/x11window.cpp b/src/x11window.cpp index 3bfa96c537..0ed0569e22 100644 --- a/src/x11window.cpp +++ b/src/x11window.cpp @@ -30,6 +30,7 @@ #include "screenedge.h" #include "shadow.h" #include "virtualdesktops.h" +#include "wayland/surface_interface.h" #include "wayland_server.h" #include "workspace.h" #include @@ -143,6 +144,26 @@ const NET::WindowTypes SUPPORTED_MANAGED_WINDOW_TYPES_MASK = NET::NormalMask | NET::CriticalNotificationMask | NET::AppletPopupMask; +// window types that are supported as unmanaged (mainly for compositing) +const NET::WindowTypes SUPPORTED_UNMANAGED_WINDOW_TYPES_MASK = NET::NormalMask + | NET::DesktopMask + | NET::DockMask + | NET::ToolbarMask + | NET::MenuMask + | NET::DialogMask + /*| NET::OverrideMask*/ + | NET::TopMenuMask + | NET::UtilityMask + | NET::SplashMask + | NET::DropdownMenuMask + | NET::PopupMenuMask + | NET::TooltipMask + | NET::NotificationMask + | NET::ComboBoxMask + | NET::DNDIconMask + | NET::OnScreenDisplayMask + | NET::CriticalNotificationMask; + X11DecorationRenderer::X11DecorationRenderer(Decoration::DecoratedClientImpl *client) : DecorationRenderer(client) , m_scheduleTimer(new QTimer(this)) @@ -319,6 +340,11 @@ X11Window::X11Window() }); } + m_releaseTimer.setSingleShot(true); + connect(&m_releaseTimer, &QTimer::timeout, this, [this]() { + releaseWindow(); + }); + // SELI TODO: Initialize xsizehints?? } @@ -335,9 +361,6 @@ X11Window::~X11Window() xcb_sync_destroy_alarm(kwinApp()->x11Connection(), m_syncRequest.alarm); } Q_ASSERT(!isInteractiveMoveResize()); - Q_ASSERT(m_client == XCB_WINDOW_NONE); - Q_ASSERT(m_wrapper == XCB_WINDOW_NONE); - Q_ASSERT(m_frame == XCB_WINDOW_NONE); Q_ASSERT(!check_active_modal); } @@ -352,6 +375,11 @@ void X11Window::deleteClient(X11Window *c) delete c; } +bool X11Window::hasScheduledRelease() const +{ + return m_releaseTimer.isActive(); +} + /** * Releases the window. The client has done its job and the window is still existing. */ @@ -361,61 +389,75 @@ void X11Window::releaseWindow(bool on_shutdown) if (SurfaceItemX11 *item = qobject_cast(surfaceItem())) { item->destroyDamage(); } + markAsDeleted(); - cleanTabBox(); - if (isInteractiveMoveResize()) { - Q_EMIT interactiveMoveResizeFinished(); - } Q_EMIT closed(); - workspace()->rulebook()->discardUsed(this, true); // Remove ForceTemporarily rules - StackingUpdatesBlocker blocker(workspace()); - if (isInteractiveMoveResize()) { - leaveInteractiveMoveResize(); + + if (isUnmanaged()) { + m_releaseTimer.stop(); + if (!findInternalWindow()) { // don't affect our own windows + if (Xcb::Extensions::self()->isShapeAvailable()) { + xcb_shape_select_input(kwinApp()->x11Connection(), window(), false); + } + Xcb::selectInput(window(), XCB_EVENT_MASK_NO_EVENT); + } + workspace()->removeUnmanaged(this); + } else { + cleanTabBox(); + if (isInteractiveMoveResize()) { + Q_EMIT interactiveMoveResizeFinished(); + } + workspace()->rulebook()->discardUsed(this, true); // Remove ForceTemporarily rules + StackingUpdatesBlocker blocker(workspace()); + if (isInteractiveMoveResize()) { + leaveInteractiveMoveResize(); + } + finishWindowRules(); + blockGeometryUpdates(); + // Grab X during the release to make removing of properties, setting to withdrawn state + // and repareting to root an atomic operation (https://lists.kde.org/?l=kde-devel&m=116448102901184&w=2) + grabXServer(); + exportMappingState(XCB_ICCCM_WM_STATE_WITHDRAWN); + setModal(false); // Otherwise its mainwindow wouldn't get focus + hidden = true; // So that it's not considered visible anymore (can't use hideClient(), it would set flags) + if (!on_shutdown) { + workspace()->windowHidden(this); + } + m_frame.unmap(); // Destroying decoration would cause ugly visual effect + cleanGrouping(); + workspace()->removeX11Window(this); + if (!on_shutdown) { + // Only when the window is being unmapped, not when closing down KWin (NETWM sections 5.5,5.7) + info->setDesktop(0); + info->setState(NET::States(), info->state()); // Reset all state flags + } + if (WinInfo *cinfo = dynamic_cast(info)) { + cinfo->disable(); + } + xcb_connection_t *c = kwinApp()->x11Connection(); + m_client.deleteProperty(atoms->kde_net_wm_user_creation_time); + m_client.deleteProperty(atoms->net_frame_extents); + m_client.deleteProperty(atoms->kde_net_wm_frame_strut); + const QPointF grav = calculateGravitation(true); + m_client.reparent(kwinApp()->x11RootWindow(), grav.x(), grav.y()); + xcb_change_save_set(c, XCB_SET_MODE_DELETE, m_client); + m_client.selectInput(XCB_EVENT_MASK_NO_EVENT); + if (on_shutdown) { + // Map the window, so it can be found after another WM is started + m_client.map(); + // TODO: Preserve minimized, shaded etc. state? + } else { // Make sure it's not mapped if the app unmapped it (#65279). The app + // may do map+unmap before we initially map the window by calling rawShow() from manage(). + m_client.unmap(); + } + m_client.reset(); + m_wrapper.reset(); + m_frame.reset(); + unblockGeometryUpdates(); // Don't use GeometryUpdatesBlocker, it would now set the geometry + ungrabXServer(); } - finishWindowRules(); - blockGeometryUpdates(); - // Grab X during the release to make removing of properties, setting to withdrawn state - // and repareting to root an atomic operation (https://lists.kde.org/?l=kde-devel&m=116448102901184&w=2) - grabXServer(); - exportMappingState(XCB_ICCCM_WM_STATE_WITHDRAWN); - setModal(false); // Otherwise its mainwindow wouldn't get focus - hidden = true; // So that it's not considered visible anymore (can't use hideClient(), it would set flags) - if (!on_shutdown) { - workspace()->windowHidden(this); - } - m_frame.unmap(); // Destroying decoration would cause ugly visual effect - cleanGrouping(); - workspace()->removeX11Window(this); - if (!on_shutdown) { - // Only when the window is being unmapped, not when closing down KWin (NETWM sections 5.5,5.7) - info->setDesktop(0); - info->setState(NET::States(), info->state()); // Reset all state flags - } - if (WinInfo *cinfo = dynamic_cast(info)) { - cinfo->disable(); - } - xcb_connection_t *c = kwinApp()->x11Connection(); - m_client.deleteProperty(atoms->kde_net_wm_user_creation_time); - m_client.deleteProperty(atoms->net_frame_extents); - m_client.deleteProperty(atoms->kde_net_wm_frame_strut); - const QPointF grav = calculateGravitation(true); - m_client.reparent(kwinApp()->x11RootWindow(), grav.x(), grav.y()); - xcb_change_save_set(c, XCB_SET_MODE_DELETE, m_client); - m_client.selectInput(XCB_EVENT_MASK_NO_EVENT); - if (on_shutdown) { - // Map the window, so it can be found after another WM is started - m_client.map(); - // TODO: Preserve minimized, shaded etc. state? - } else { // Make sure it's not mapped if the app unmapped it (#65279). The app - // may do map+unmap before we initially map the window by calling rawShow() from manage(). - m_client.unmap(); - } - m_client.reset(); - m_wrapper.reset(); - m_frame.reset(); - unblockGeometryUpdates(); // Don't use GeometryUpdatesBlocker, it would now set the geometry + unref(); - ungrabXServer(); } /** @@ -428,34 +470,113 @@ void X11Window::destroyWindow() if (SurfaceItemX11 *item = qobject_cast(surfaceItem())) { item->forgetDamage(); } + markAsDeleted(); - cleanTabBox(); - if (isInteractiveMoveResize()) { - Q_EMIT interactiveMoveResizeFinished(); - } Q_EMIT closed(); - workspace()->rulebook()->discardUsed(this, true); // Remove ForceTemporarily rules - StackingUpdatesBlocker blocker(workspace()); - if (isInteractiveMoveResize()) { - leaveInteractiveMoveResize(); + + if (isUnmanaged()) { + m_releaseTimer.stop(); + workspace()->removeUnmanaged(this); + } else { + cleanTabBox(); + if (isInteractiveMoveResize()) { + Q_EMIT interactiveMoveResizeFinished(); + } + workspace()->rulebook()->discardUsed(this, true); // Remove ForceTemporarily rules + StackingUpdatesBlocker blocker(workspace()); + if (isInteractiveMoveResize()) { + leaveInteractiveMoveResize(); + } + finishWindowRules(); + blockGeometryUpdates(); + setModal(false); + hidden = true; // So that it's not considered visible anymore + workspace()->windowHidden(this); + cleanGrouping(); + workspace()->removeX11Window(this); + if (WinInfo *cinfo = dynamic_cast(info)) { + cinfo->disable(); + } + m_client.reset(); // invalidate + m_wrapper.reset(); + m_frame.reset(); + unblockGeometryUpdates(); // Don't use GeometryUpdatesBlocker, it would now set the geometry } - finishWindowRules(); - blockGeometryUpdates(); - setModal(false); - hidden = true; // So that it's not considered visible anymore - workspace()->windowHidden(this); - cleanGrouping(); - workspace()->removeX11Window(this); - if (WinInfo *cinfo = dynamic_cast(info)) { - cinfo->disable(); - } - m_client.reset(); // invalidate - m_wrapper.reset(); - m_frame.reset(); - unblockGeometryUpdates(); // Don't use GeometryUpdatesBlocker, it would now set the geometry + unref(); } +bool X11Window::track(xcb_window_t w) +{ + XServerGrabber xserverGrabber; + Xcb::WindowAttributes attr(w); + Xcb::WindowGeometry geo(w); + if (attr.isNull() || attr->map_state != XCB_MAP_STATE_VIEWABLE) { + return false; + } + if (attr->_class == XCB_WINDOW_CLASS_INPUT_ONLY) { + return false; + } + if (geo.isNull()) { + return false; + } + + m_unmanaged = true; + + setWindowHandles(w); + m_frame.reset(w, false); + m_wrapper.reset(w, false); + m_client.reset(w, false); + + Xcb::selectInput(w, attr->your_event_mask | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE); + m_bufferGeometry = geo.rect(); + m_frameGeometry = geo.rect(); + m_clientGeometry = geo.rect(); + checkOutput(); + m_visual = attr->visual; + bit_depth = geo->depth; + info = new NETWinInfo(kwinApp()->x11Connection(), w, kwinApp()->x11RootWindow(), + NET::WMWindowType | NET::WMPid, + NET::WM2Opacity | NET::WM2WindowRole | NET::WM2WindowClass | NET::WM2OpaqueRegion); + setOpacity(info->opacityF()); + getResourceClass(); + getWmClientLeader(); + getWmClientMachine(); + if (Xcb::Extensions::self()->isShapeAvailable()) { + xcb_shape_select_input(kwinApp()->x11Connection(), w, true); + } + detectShape(w); + getWmOpaqueRegion(); + getSkipCloseAnimation(); + setupCompositing(); + if (QWindow *internalWindow = findInternalWindow()) { + m_outline = internalWindow->property("__kwin_outline").toBool(); + } + if (effects) { + static_cast(effects)->checkInputWindowStacking(); + } + + switch (kwinApp()->operationMode()) { + case Application::OperationModeXwayland: + // The wayland surface is associated with the override-redirect window asynchronously. + if (surface()) { + associate(); + } else { + connect(this, &Window::surfaceChanged, this, &X11Window::associate); + } + break; + case Application::OperationModeX11: + // We have no way knowing whether the override-redirect window can be painted. Mark it + // as ready for painting after synthetic 50ms delay. + QTimer::singleShot(50, this, &X11Window::setReadyForPainting); + break; + case Application::OperationModeWaylandOnly: + Q_UNREACHABLE(); + } + + return true; +} + /** * Manages the clients. This means handling the very first maprequest: * reparenting, initial geometry, initial state, placement, etc. @@ -1333,6 +1454,9 @@ bool X11Window::userNoBorder() const bool X11Window::isFullScreenable() const { + if (isUnmanaged()) { + return false; + } if (!rules()->checkFullScreen(true)) { return false; } @@ -1355,6 +1479,10 @@ bool X11Window::noBorder() const bool X11Window::userCanSetNoBorder() const { + if (isUnmanaged()) { + return false; + } + // Client-side decorations and server-side decorations are mutually exclusive. if (isClientSideDecorated()) { return false; @@ -1657,7 +1785,7 @@ void X11Window::doSetShade(ShadeMode previousShadeMode) void X11Window::updateVisibility() { - if (isDeleted()) { + if (isUnmanaged() || isDeleted()) { return; } if (hidden) { @@ -1872,7 +2000,7 @@ void X11Window::sendClientMessage(xcb_window_t w, xcb_atom_t a, xcb_atom_t proto */ bool X11Window::isCloseable() const { - return rules()->checkCloseable(m_motif.close() && !isSpecialWindow()); + return !isUnmanaged() && rules()->checkCloseable(m_motif.close() && !isSpecialWindow()); } /** @@ -1901,10 +2029,14 @@ void X11Window::closeWindow() */ void X11Window::killWindow() { - qCDebug(KWIN_CORE) << "X11Window::killWindow():" << caption(); - killProcess(false); - m_client.kill(); // Always kill this client at the server - destroyWindow(); + qCDebug(KWIN_CORE) << "X11Window::killWindow():" << window(); + if (isUnmanaged()) { + xcb_kill_client(kwinApp()->x11Connection(), window()); + } else { + killProcess(false); + m_client.kill(); // Always kill this client at the server + destroyWindow(); + } } /** @@ -2580,11 +2712,25 @@ QString X11Window::preferredColorScheme() const bool X11Window::isClient() const { - return true; + return !m_unmanaged; +} + +bool X11Window::isUnmanaged() const +{ + return m_unmanaged; +} + +bool X11Window::isOutline() const +{ + return m_outline; } NET::WindowType X11Window::windowType(bool direct) const { + if (m_unmanaged) { + return info->windowType(SUPPORTED_UNMANAGED_WINDOW_TYPES_MASK); + } + NET::WindowType wt = info->windowType(SUPPORTED_MANAGED_WINDOW_TYPES_MASK); if (direct) { return wt; @@ -4030,6 +4176,9 @@ void X11Window::GTKShowWindowMenu(qreal x_root, qreal y_root) bool X11Window::isMovable() const { + if (isUnmanaged()) { + return false; + } if (!hasNETSupport() && !m_motif.move()) { return false; } @@ -4047,6 +4196,9 @@ bool X11Window::isMovable() const bool X11Window::isMovableAcrossScreens() const { + if (isUnmanaged()) { + return false; + } if (!hasNETSupport() && !m_motif.move()) { return false; } @@ -4061,6 +4213,9 @@ bool X11Window::isMovableAcrossScreens() const bool X11Window::isResizable() const { + if (isUnmanaged()) { + return false; + } if (!hasNETSupport() && !m_motif.resize()) { return false; } @@ -4085,6 +4240,9 @@ bool X11Window::isResizable() const bool X11Window::isMaximizable() const { + if (isUnmanaged()) { + return false; + } if (!isResizable() || isToolbar()) { // SELI isToolbar() ? return false; } @@ -4113,6 +4271,11 @@ void X11Window::moveResizeInternal(const QRectF &rect, MoveResizeMode mode) // Such code is wrong and should be changed to handle the case when the window is shaded, // for example using X11Window::clientSize() + if (isUnmanaged()) { + qCWarning(KWIN_CORE) << "Cannot move or resize unmanaged window" << this; + return; + } + QRectF frameGeometry = Xcb::fromXNative(Xcb::toXNative(rect)); if (shade_geometry_change) { @@ -4215,6 +4378,11 @@ void X11Window::updateServerGeometry() static bool changeMaximizeRecursion = false; void X11Window::maximize(MaximizeMode mode) { + if (isUnmanaged()) { + qCWarning(KWIN_CORE) << "Cannot change maximized state of unmanaged window" << this; + return; + } + if (changeMaximizeRecursion) { return; } @@ -4487,6 +4655,9 @@ void X11Window::maximize(MaximizeMode mode) bool X11Window::userCanSetFullScreen() const { + if (isUnmanaged()) { + return false; + } if (!isFullScreenable()) { return false; } @@ -4773,7 +4944,7 @@ void X11Window::applyWindowRules() bool X11Window::supportsWindowRules() const { - return true; + return !isUnmanaged(); } void X11Window::updateWindowRules(Rules::Types selection) @@ -4814,4 +4985,31 @@ void X11Window::updateWindowPixmap() } } +void X11Window::associate() +{ + if (surface()->isMapped()) { + setReadyForPainting(); + } else { + // Queued connection because we want to mark the window ready for painting after + // the associated surface item has processed the new surface state. + connect(surface(), &KWaylandServer::SurfaceInterface::mapped, this, &X11Window::setReadyForPainting, Qt::QueuedConnection); + } +} + +QWindow *X11Window::findInternalWindow() const +{ + const QWindowList windows = kwinApp()->topLevelWindows(); + for (QWindow *w : windows) { + if (w->handle() && w->winId() == window()) { + return w; + } + } + return nullptr; +} + +void X11Window::checkOutput() +{ + setOutput(workspace()->outputAt(frameGeometry().center())); +} + } // namespace diff --git a/src/x11window.h b/src/x11window.h index 6c486660b2..f6e77e9c62 100644 --- a/src/x11window.h +++ b/src/x11window.h @@ -120,8 +120,12 @@ public: bool windowEvent(xcb_generic_event_t *e); NET::WindowType windowType(bool direct = false) const override; + bool track(xcb_window_t w); bool manage(xcb_window_t w, bool isMapped); + void releaseWindow(bool on_shutdown = false); + bool hasScheduledRelease() const; + void destroyWindow() override; QStringList activities() const override; @@ -248,6 +252,8 @@ public: // sets whether the client should be faked as being on all activities (and be shown during session save) void setSessionActivityOverride(bool needed); bool isClient() const override; + bool isOutline() const override; + bool isUnmanaged() const override; void cancelFocusOutTimer(); @@ -297,6 +303,7 @@ private: bool mapRequestEvent(xcb_map_request_event_t *e); void unmapNotifyEvent(xcb_unmap_notify_event_t *e); void destroyNotifyEvent(xcb_destroy_notify_event_t *e); + void configureNotifyEvent(xcb_configure_notify_event_t *e); void configureRequestEvent(xcb_configure_request_event_t *e); void propertyNotifyEvent(xcb_property_notify_event_t *e) override; void clientMessageEvent(xcb_client_message_event_t *e) override; @@ -407,6 +414,10 @@ private: void createDecoration(const QRectF &oldgeom); void destroyDecoration(); + QWindow *findInternalWindow() const; + void checkOutput(); + void associate(); + Xcb::Window m_client; Xcb::Window m_wrapper; Xcb::Window m_frame; @@ -477,6 +488,7 @@ private: QPointF input_offset; QTimer *m_focusOutTimer; + QTimer m_releaseTimer; QMetaObject::Connection m_edgeRemoveConnection; QMetaObject::Connection m_edgeGeometryTrackingConnection; @@ -487,6 +499,9 @@ private: QRectF m_lastFrameGeometry; QRectF m_lastClientGeometry; std::unique_ptr m_decorationRenderer; + + bool m_unmanaged = false; + bool m_outline = false; }; inline xcb_window_t X11Window::wrapperId() const