kwin/src/unmanaged.cpp
Vlad Zahorodnii 510a41eeb8 Ensure that Toplevel::output() stays always in sync with geometry
Currently, if geometry updates are blocked, the Toplevel.output property
won't be updated. On the other hand, it's reasonable to use the output
property instead of manually looking up the output in window management
code, e.g. Workspace::clientArea().

In other words, using the Toplevel.output property is like walking on a
mine field, things can blow up. You can't use Toplevel.output even if it
makes perfect sense.

This change ensures that Toplevel.output property is always kept in sync
with the frame geometry. Unfortunately, this means that the output
property no longer can be updated when the frameGeometryChanged() signal
is emitted. It has to be done in moveResizeInternal() method.
2022-04-08 09:53:13 +00:00

237 lines
6.5 KiB
C++

/*
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 "deleted.h"
#include "effects.h"
#include "platform.h"
#include "surfaceitem_x11.h"
#include "utils/common.h"
#include "workspace.h"
#include <QDebug>
#include <QTimer>
#include <QWidget>
#include <QWindow>
#include <xcb/shape.h>
#include <KWaylandServer/surface_interface.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()
: Toplevel()
{
switch (kwinApp()->operationMode()) {
case Application::OperationModeXwayland:
// The wayland surface is associated with the override-redirect window asynchronously.
connect(this, &Toplevel::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()
{
}
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();
// With unmanaged windows there is a race condition between the client painting the window
// and us setting up damage tracking. If the client wins we won't get a damage event even
// though the window has been painted. To avoid this we mark the whole window as damaged
// and schedule a repaint immediately after creating the damage object.
if (auto item = surfaceItem()) {
item->addDamage(item->rect());
}
}
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)
{
Deleted *del = nullptr;
if (releaseReason != ReleaseReason::KWinShutsDown) {
del = Deleted::create(this);
}
Q_EMIT windowClosed(this, del);
finishCompositing(releaseReason);
if (!QWidget::find(window()) && releaseReason != ReleaseReason::Destroyed) { // 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);
if (releaseReason != ReleaseReason::KWinShutsDown) {
disownDataPassedToDeleted();
del->unrefWindow();
}
deleteUnmanaged(this);
}
void Unmanaged::deleteUnmanaged(Unmanaged *c)
{
delete c;
}
bool Unmanaged::hasScheduledRelease() const
{
return m_scheduledRelease;
}
int Unmanaged::desktop() const
{
return NET::OnAllDesktops; // TODO for some window types should be the current desktop?
}
QStringList Unmanaged::activities() const
{
return QStringList();
}
QVector<VirtualDesktop *> Unmanaged::desktops() const
{
return QVector<VirtualDesktop *>();
}
QPoint Unmanaged::clientPos() const
{
return QPoint(0, 0); // unmanaged windows don't have decorations
}
NET::WindowType Unmanaged::windowType(bool direct, int supportedTypes) const
{
// for unmanaged windows the direct does not make any difference
// as there are no rules to check and no hacks to apply
Q_UNUSED(direct)
if (supportedTypes == 0) {
supportedTypes = SUPPORTED_UNMANAGED_WINDOW_TYPES_MASK;
}
return info->windowType(NET::WindowTypes(supportedTypes));
}
bool Unmanaged::isOutline() const
{
return m_outline;
}
QWindow *Unmanaged::findInternalWindow() const
{
const QWindowList windows = kwinApp()->topLevelWindows();
for (QWindow *w : windows) {
if (w->winId() == window()) {
return w;
}
}
return nullptr;
}
void Unmanaged::checkOutput()
{
setOutput(kwinApp()->platform()->outputAt(frameGeometry().center()));
}
void Unmanaged::damageNotifyEvent()
{
Q_ASSERT(kwinApp()->operationMode() == Application::OperationModeX11);
SurfaceItemX11 *item = static_cast<SurfaceItemX11 *>(surfaceItem());
if (item) {
item->processDamage();
}
}
} // namespace