From dbbcf31d0d0f14106ac6e50b45df8b1e267edaed Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Tue, 28 Mar 2023 14:38:19 +0300 Subject: [PATCH] x11: Merge Unmanaged into X11Window Currently, managed and override-redirect windows are split in two types: X11Window and Unmanaged. While looking at it strictly from type perspective, this is great. But it creates other problems, e.g. we need to put shared X11-specific code in the base Window class or mess with "base" classes. As an alternative solution, this change merges the Unmanaged class into the X11Window class and disables some functionality based on the value of isUnmanaged(). X11Window::manage() is used to create a managed Window. X11Window::track() is used to create an unmanaged Window. --- .../integration/xwaylandserver_crash_test.cpp | 4 +- src/CMakeLists.txt | 1 - .../x11_standalone_windowselector.cpp | 1 - src/composite.cpp | 1 - src/dbusinterface.cpp | 1 - src/debug_console.cpp | 27 +- src/debug_console.h | 5 +- src/effects.cpp | 5 +- src/events.cpp | 188 +++++---- src/input.cpp | 1 - src/killwindow.cpp | 1 - src/layers.cpp | 11 +- src/scene/workspacescene.cpp | 1 - src/sm.cpp | 4 +- src/tabbox/tabbox.cpp | 1 - src/unmanaged.cpp | 226 ----------- src/unmanaged.h | 79 ---- src/wayland_server.cpp | 5 +- src/window_property_notify_x11_filter.cpp | 1 - src/workspace.cpp | 33 +- src/workspace.h | 11 +- src/x11window.cpp | 366 ++++++++++++++---- src/x11window.h | 15 + 23 files changed, 433 insertions(+), 555 deletions(-) delete mode 100644 src/unmanaged.cpp delete mode 100644 src/unmanaged.h 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