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.
This commit is contained in:
Vlad Zahorodnii 2023-03-28 14:38:19 +03:00
parent 062f092f38
commit dbbcf31d0d
23 changed files with 433 additions and 555 deletions

View file

@ -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<Unmanaged *>();
qRegisterMetaType<X11Window *>();
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> unmanaged = unmanagedAddedSpy.last().first().value<Unmanaged *>();
QPointer<X11Window> unmanaged = unmanagedAddedSpy.last().first().value<X11Window *>();
QVERIFY(unmanaged);
// Let's pretend that the Xwayland process has crashed.

View file

@ -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

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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<X11Window *>(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<Unmanaged *>(window)) {
add(s_x11UnmanagedId - 1, m_unmanageds, unmanaged);
return;
}
}
void DebugConsoleModel::handleWindowRemoved(Window *window)
{
if (auto x11 = qobject_cast<X11Window *>(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<Unmanaged *>(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);
}

View file

@ -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<WaylandWindow *> m_waylandWindows;
QVector<InternalWindow *> m_internalWindows;
QVector<X11Window *> m_x11Windows;
QVector<Unmanaged *> m_unmanageds;
QVector<X11Window *> m_unmanageds;
};
class DebugConsoleDelegate : public QStyledItemDelegate

View file

@ -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<KWin::WaylandWindow *>(window) != nullptr;
m_x11Window = qobject_cast<KWin::X11Window *>(window) != nullptr || qobject_cast<KWin::Unmanaged *>(window) != nullptr;
m_x11Window = qobject_cast<KWin::X11Window *>(window) != nullptr;
}
EffectWindowImpl::~EffectWindowImpl()

View file

@ -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<xcb_map_notify_event_t *>(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<xcb_configure_notify_event_t *>(e));
break;
case XCB_PROPERTY_NOTIFY:
propertyNotifyEvent(reinterpret_cast<xcb_property_notify_event_t *>(e));
break;
case XCB_CLIENT_MESSAGE:
clientMessageEvent(reinterpret_cast<xcb_client_message_event_t *>(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<EffectsHandlerImpl *>(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<xcb_configure_notify_event_t *>(e));
break;
case XCB_PROPERTY_NOTIFY:
propertyNotifyEvent(reinterpret_cast<xcb_property_notify_event_t *>(e));
break;
case XCB_CLIENT_MESSAGE:
clientMessageEvent(reinterpret_cast<xcb_client_message_event_t *>(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<EffectsHandlerImpl *>(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
// ****************************************

View file

@ -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"

View file

@ -11,7 +11,6 @@
#include "killwindow.h"
#include "main.h"
#include "osd.h"
#include "unmanaged.h"
#include "window.h"
#include <KLocalizedString>

View file

@ -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<X11Window *>(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<X11Window *>(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<X11Window *>(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<X11Window *>(*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<X11Window *>(*it);
if (!current) {
if (!current || current->isUnmanaged()) {
continue;
}
if (current->sessionStackingOrder() > window->sessionStackingOrder()) {

View file

@ -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"

View file

@ -91,7 +91,7 @@ void SessionManager::storeSession(const QString &sessionName, SMSavePhase phase)
const QList<Window *> windows = workspace()->windows();
for (auto it = windows.begin(); it != windows.end(); ++it) {
X11Window *c = qobject_cast<X11Window *>(*it);
if (!c) {
if (!c || c->isUnmanaged()) {
continue;
}
if (c->windowType() > NET::Splash) {
@ -180,7 +180,7 @@ void SessionManager::storeSubSession(const QString &name, QSet<QByteArray> sessi
for (auto it = windows.begin(); it != windows.end(); ++it) {
X11Window *c = qobject_cast<X11Window *>(*it);
if (!c) {
if (!c || c->isUnmanaged()) {
continue;
}
if (c->windowType() > NET::Splash) {

View file

@ -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"

View file

@ -1,226 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
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 <QDebug>
#include <QTimer>
#include <QWindow>
#include <xcb/shape.h>
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<WindowItem> Unmanaged::createItem(Scene *scene)
{
return std::make_unique<WindowItemX11>(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<EffectsHandlerImpl *>(effects)->checkInputWindowStacking();
}
return true;
}
void Unmanaged::release(ReleaseReason releaseReason)
{
if (SurfaceItemX11 *item = qobject_cast<SurfaceItemX11 *>(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<SurfaceItemX11 *>(surfaceItem());
if (item) {
item->processDamage();
}
}
void Unmanaged::killWindow()
{
xcb_kill_client(kwinApp()->x11Connection(), window());
}
} // namespace

View file

@ -1,79 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <netwm.h>
#include "window.h"
#include <QTimer>
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<WindowItem> 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

View file

@ -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) {

View file

@ -8,7 +8,6 @@
*/
#include "window_property_notify_x11_filter.h"
#include "effects.h"
#include "unmanaged.h"
#include "workspace.h"
#include "x11window.h"

View file

@ -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<X11Window *>(window)) {
x11->releaseWindow(true);
} else if (auto unmanaged = qobject_cast<Unmanaged *>(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<void(X11Window *)> func)
{
for (Window *window : std::as_const(m_windows)) {
X11Window *x11Window = qobject_cast<X11Window *>(window);
if (x11Window) {
if (x11Window && !x11Window->isUnmanaged()) {
func(x11Window);
}
}
@ -1904,27 +1899,27 @@ X11Window *Workspace::findClient(std::function<bool(const X11Window *)> func) co
{
for (Window *window : std::as_const(m_windows)) {
X11Window *x11Window = qobject_cast<X11Window *>(window);
if (x11Window && func(x11Window)) {
if (x11Window && !x11Window->isUnmanaged() && func(x11Window)) {
return x11Window;
}
}
return nullptr;
}
Unmanaged *Workspace::findUnmanaged(std::function<bool(const Unmanaged *)> func) const
X11Window *Workspace::findUnmanaged(std::function<bool(const X11Window *)> func) const
{
for (Window *window : m_windows) {
Unmanaged *unmanaged = qobject_cast<Unmanaged *>(window);
if (unmanaged && func(unmanaged)) {
return unmanaged;
X11Window *x11Window = qobject_cast<X11Window *>(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;
});
}

View file

@ -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<void(X11Window *)> func);
Unmanaged *findUnmanaged(std::function<bool(const Unmanaged *)> func) const;
X11Window *findUnmanaged(std::function<bool(const X11Window *)> 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<bool(const Window *)> 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);

View file

@ -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 <KDecoration2/DecoratedClient>
@ -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<SurfaceItemX11 *>(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<WinInfo *>(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<WinInfo *>(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<SurfaceItemX11 *>(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<WinInfo *>(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<WinInfo *>(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<EffectsHandlerImpl *>(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

View file

@ -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<X11DecorationRenderer> m_decorationRenderer;
bool m_unmanaged = false;
bool m_outline = false;
};
inline xcb_window_t X11Window::wrapperId() const