Commit 47113e09 authored by Vlad Zahorodnii's avatar Vlad Zahorodnii
Browse files

scene: Introduce window items

Currently, dealing with sub-surfaces is very difficult due to the scene
design being heavily influenced by X11 requirements.

The goal of this change is to re-work scene abstractions to make improving
the wayland support easier.

The Item class is based on the QQuickItem class. My hope is that one day
we will be able to transition to QtQuick for painting scene, but in
meanwhile it makes more sense to have a minimalistic internal item class.

The WindowItem class represents a window. The SurfaceItem class represents
the contents of either an X11, or a Wayland, or an internal surface. The
DecorationItem and the ShadowItem class represent the server-side deco and
drop-shadow, respectively.

At the moment, the SurfaceItem is bound to the scene window, but the long
term plan is to break that connection so we could re-use the SurfaceItem
for things such as software cursors and drag-and-drop additional icons.

One of the responsibilities of the Item is to schedule repaints as needed.
Ideally, there shouldn't be any addRepaint() calls in the core code. The
Item class schedules repaints on geometry updates. In the future, it also
has to request an update if its opacity or visibility changes.
parent b61c800c
......@@ -33,6 +33,7 @@ set(kwin_SRCS
cursor.cpp
dbusinterface.cpp
debug_console.cpp
decorationitem.cpp
decorations/decoratedclient.cpp
decorations/decorationbridge.cpp
decorations/decorationpalette.cpp
......@@ -59,6 +60,7 @@ set(kwin_SRCS
inputpanelv1client.cpp
inputpanelv1integration.cpp
internal_client.cpp
item.cpp
keyboard_input.cpp
keyboard_layout.cpp
keyboard_layout_switching.cpp
......@@ -111,8 +113,13 @@ set(kwin_SRCS
session_logind.cpp
session_noop.cpp
shadow.cpp
shadowitem.cpp
sm.cpp
subsurfacemonitor.cpp
surfaceitem.cpp
surfaceitem_internal.cpp
surfaceitem_wayland.cpp
surfaceitem_x11.cpp
syncalarmx11filter.cpp
tablet_input.cpp
thumbnailitem.cpp
......@@ -131,6 +138,7 @@ set(kwin_SRCS
waylandclient.cpp
waylandshellintegration.cpp
window_property_notify_x11_filter.cpp
windowitem.cpp
workspace.cpp
x11client.cpp
x11eventfilter.cpp
......
......@@ -2370,10 +2370,16 @@ void AbstractClient::createDecoration(const QRect &oldGeometry)
void AbstractClient::destroyDecoration()
{
delete m_decoration.decoration;
m_decoration.decoration = nullptr;
setDecoration(nullptr);
m_decoration.inputRegion = QRegion();
}
void AbstractClient::setDecoration(KDecoration2::Decoration *decoration)
{
m_decoration.decoration = decoration;
emit decorationChanged();
}
void AbstractClient::updateDecorationInputShape()
{
if (!isDecorated()) {
......
......@@ -925,6 +925,7 @@ Q_SIGNALS:
void hasApplicationMenuChanged(bool);
void applicationMenuActiveChanged(bool);
void unresponsiveChanged(bool);
void decorationChanged();
protected:
AbstractClient();
......@@ -1201,9 +1202,7 @@ protected:
s_haveResizeEffect = false;
}
void setDecoration(KDecoration2::Decoration *decoration) {
m_decoration.decoration = decoration;
}
void setDecoration(KDecoration2::Decoration *decoration);
virtual void createDecoration(const QRect &oldGeometry);
virtual void destroyDecoration();
void startDecorationDoubleClickTimer();
......
......@@ -21,6 +21,7 @@
#include "scene.h"
#include "screens.h"
#include "shadow.h"
#include "surfaceitem_x11.h"
#include "unmanaged.h"
#include "useractions.h"
#include "utils.h"
......@@ -800,26 +801,25 @@ void X11Compositor::composite(RenderLoop *renderLoop)
}
QList<Toplevel *> windows = Workspace::self()->xStackingOrder();
QList<Toplevel *> damaged;
QList<SurfaceItemX11 *> dirtyItems;
// Reset the damage state of each window and fetch the damage region
// without waiting for a reply
for (Toplevel *win : qAsConst(windows)) {
if (win->resetAndFetchDamage()) {
damaged << win;
for (Toplevel *window : qAsConst(windows)) {
SurfaceItemX11 *surfaceItem = static_cast<SurfaceItemX11 *>(window->surfaceItem());
if (surfaceItem->fetchDamage()) {
dirtyItems.append(surfaceItem);
}
}
if (damaged.count() > 0) {
if (dirtyItems.count() > 0) {
scene()->triggerFence();
if (auto c = kwinApp()->x11Connection()) {
xcb_flush(c);
}
xcb_flush(kwinApp()->x11Connection());
}
// Get the replies
for (Toplevel *window : qAsConst(damaged)) {
window->getDamageRegionReply();
for (SurfaceItemX11 *item : qAsConst(dirtyItems)) {
item->waitForDamage();
}
Compositor::composite(renderLoop);
......
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "decorationitem.h"
#include <KDecoration2/Decoration>
namespace KWin
{
DecorationItem::DecorationItem(KDecoration2::Decoration *decoration, Scene::Window *window, Item *parent)
: Item(window, parent)
{
Toplevel *toplevel = window->window();
connect(toplevel, &Toplevel::frameGeometryChanged,
this, &DecorationItem::handleFrameGeometryChanged);
connect(toplevel, &Toplevel::screenScaleChanged,
this, &DecorationItem::discardQuads);
connect(decoration, &KDecoration2::Decoration::bordersChanged,
this, &DecorationItem::discardQuads);
setSize(toplevel->size());
}
void DecorationItem::handleFrameGeometryChanged()
{
setSize(window()->size());
}
} // namespace KWin
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "item.h"
namespace KDecoration2
{
class Decoration;
}
namespace KWin
{
/**
* The DecorationItem class represents a server-side decoration.
*/
class KWIN_EXPORT DecorationItem : public Item
{
Q_OBJECT
public:
explicit DecorationItem(KDecoration2::Decoration *decoration, Scene::Window *window, Item *parent = nullptr);
private Q_SLOTS:
void handleFrameGeometryChanged();
private:
QPointer<KDecoration2::Decoration> m_decoration;
};
} // namespace KWin
......@@ -31,6 +31,7 @@
#include "scripting/scriptedeffect.h"
#include "screens.h"
#include "screenlockerwatcher.h"
#include "surfaceitem.h"
#include "thumbnailitem.h"
#include "virtualdesktops.h"
#include "window_property_notify_x11_filter.h"
......@@ -2024,7 +2025,7 @@ void EffectWindowImpl::setSceneWindow(Scene::Window* w)
QRegion EffectWindowImpl::shape() const
{
if (isX11Client() && sceneWindow()) {
return sceneWindow()->bufferShape();
return sceneWindow()->surfaceItem()->shape();
}
return toplevel->rect();
}
......
......@@ -1253,16 +1253,12 @@ void Unmanaged::configureNotifyEvent(xcb_configure_notify_event_t *e)
static_cast<EffectsHandlerImpl*>(effects)->checkInputWindowStacking(); // keep them on top
QRect newgeom(e->x, e->y, e->width, e->height);
if (newgeom != m_frameGeometry) {
addWorkspaceRepaint(visibleGeometry()); // damage old area
QRect old = m_frameGeometry;
m_clientGeometry = newgeom;
m_frameGeometry = newgeom;
emit bufferGeometryChanged(this, old);
emit clientGeometryChanged(this, old);
emit frameGeometryChanged(this, old);
addRepaintFull();
if (old.size() != m_frameGeometry.size())
discardWindowPixmap();
emit geometryShapeChanged(this, old);
}
}
......
......@@ -10,6 +10,7 @@
#include "internal_client.h"
#include "decorations/decorationbridge.h"
#include "deleted.h"
#include "surfaceitem.h"
#include "workspace.h"
#include <KDecoration2/Decoration>
......@@ -378,7 +379,7 @@ void InternalClient::present(const QSharedPointer<QOpenGLFramebufferObject> fbo)
}
setDepth(32);
addDamageFull();
surfaceItem()->addDamage(surfaceItem()->rect());
}
void InternalClient::present(const QImage &image, const QRegion &damage)
......@@ -397,7 +398,7 @@ void InternalClient::present(const QImage &image, const QRegion &damage)
m_internalImage = image;
setDepth(32);
addDamage(damage);
surfaceItem()->addDamage(damage);
}
QWindow *InternalClient::internalWindow() const
......
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "item.h"
namespace KWin
{
Item::Item(Scene::Window *window, Item *parent)
: m_window(window)
{
setParentItem(parent);
}
Item::~Item()
{
setParentItem(nullptr);
}
int Item::x() const
{
return m_x;
}
void Item::setX(int x)
{
if (m_x == x) {
return;
}
scheduleRepaint(boundingRect());
m_x = x;
scheduleRepaint(boundingRect());
discardQuads();
emit xChanged();
}
int Item::y() const
{
return m_y;
}
void Item::setY(int y)
{
if (m_y == y) {
return;
}
scheduleRepaint(boundingRect());
m_y = y;
scheduleRepaint(boundingRect());
discardQuads();
emit yChanged();
}
int Item::width() const
{
return m_width;
}
void Item::setWidth(int width)
{
if (m_width == width) {
return;
}
scheduleRepaint(rect());
m_width = width;
updateBoundingRect();
scheduleRepaint(rect());
discardQuads();
emit widthChanged();
}
int Item::height() const
{
return m_height;
}
void Item::setHeight(int height)
{
if (m_height == height) {
return;
}
scheduleRepaint(rect());
m_height = height;
updateBoundingRect();
scheduleRepaint(rect());
discardQuads();
emit heightChanged();
}
Item *Item::parentItem() const
{
return m_parentItem;
}
void Item::setParentItem(Item *item)
{
if (m_parentItem == item) {
return;
}
if (m_parentItem) {
m_parentItem->removeChild(this);
}
m_parentItem = item;
if (m_parentItem) {
m_parentItem->addChild(this);
}
}
void Item::addChild(Item *item)
{
Q_ASSERT(!m_childItems.contains(item));
connect(item, &Item::xChanged, this, &Item::updateBoundingRect);
connect(item, &Item::yChanged, this, &Item::updateBoundingRect);
connect(item, &Item::boundingRectChanged, this, &Item::updateBoundingRect);
m_childItems.append(item);
updateBoundingRect();
scheduleRepaint(item->boundingRect().translated(item->position()));
discardQuads();
}
void Item::removeChild(Item *item)
{
Q_ASSERT(m_childItems.contains(item));
scheduleRepaint(item->boundingRect().translated(item->position()));
m_childItems.removeOne(item);
disconnect(item, &Item::xChanged, this, &Item::updateBoundingRect);
disconnect(item, &Item::yChanged, this, &Item::updateBoundingRect);
disconnect(item, &Item::boundingRectChanged, this, &Item::updateBoundingRect);
updateBoundingRect();
discardQuads();
}
QList<Item *> Item::childItems() const
{
return m_childItems;
}
Scene::Window *Item::window() const
{
return m_window;
}
QPoint Item::position() const
{
return QPoint(x(), y());
}
void Item::setPosition(const QPoint &point)
{
const bool xDirty = (x() != point.x());
const bool yDirty = (y() != point.y());
if (xDirty || yDirty) {
scheduleRepaint(boundingRect());
m_x = point.x();
m_y = point.y();
scheduleRepaint(boundingRect());
discardQuads();
if (xDirty) {
emit xChanged();
}
if (yDirty) {
emit yChanged();
}
}
}
QSize Item::size() const
{
return QSize(width(), height());
}
void Item::setSize(const QSize &size)
{
const bool widthDirty = (width() != size.width());
const bool heightDirty = (height() != size.height());
if (widthDirty || heightDirty) {
scheduleRepaint(rect());
m_width = size.width();
m_height = size.height();
updateBoundingRect();
scheduleRepaint(rect());
discardQuads();
if (widthDirty) {
emit widthChanged();
}
if (heightDirty) {
emit heightChanged();
}
}
}
QRect Item::rect() const
{
return QRect(QPoint(0, 0), size());
}
QRect Item::boundingRect() const
{
return m_boundingRect;
}
void Item::updateBoundingRect()
{
QRect boundingRect = rect();
for (Item *item : qAsConst(m_childItems)) {
boundingRect |= item->boundingRect().translated(item->position());
}
if (m_boundingRect != boundingRect) {
m_boundingRect = boundingRect;
emit boundingRectChanged();
}
}
QPoint Item::rootPosition() const
{
QPoint ret = position();
Item *parent = parentItem();
while (parent) {
ret += parent->position();
parent = parent->parentItem();
}
return ret;
}
QRegion Item::mapToGlobal(const QRegion &region) const
{
return region.translated(rootPosition());
}
QRect Item::mapToGlobal(const QRect &rect) const
{
return rect.translated(rootPosition());
}
void Item::stackBefore(Item *sibling)
{
if (Q_UNLIKELY(!sibling)) {
qCDebug(KWIN_CORE) << Q_FUNC_INFO << "requires a valid sibling";
return;
}
if (Q_UNLIKELY(!sibling->parentItem() || sibling->parentItem() != parentItem())) {
qCDebug(KWIN_CORE) << Q_FUNC_INFO << "requires items to be siblings";
return;
}
if (Q_UNLIKELY(sibling == this)) {
return;
}
const int selfIndex = m_parentItem->m_childItems.indexOf(this);
const int siblingIndex = m_parentItem->m_childItems.indexOf(sibling);
if (selfIndex == siblingIndex - 1) {
return;
}
m_parentItem->m_childItems.move(selfIndex, selfIndex > siblingIndex ? siblingIndex : siblingIndex - 1);
scheduleRepaint(boundingRect());
sibling->scheduleRepaint(sibling->boundingRect());
discardQuads();
}
void Item::stackAfter(Item *sibling)
{
if (Q_UNLIKELY(!sibling)) {
qCDebug(KWIN_CORE) << Q_FUNC_INFO << "requires a valid sibling";
return;
}
if (Q_UNLIKELY(!sibling->parentItem() || sibling->parentItem() != parentItem())) {
qCDebug(KWIN_CORE) << Q_FUNC_INFO << "requires items to be siblings";
return;
}
if (Q_UNLIKELY(sibling == this)) {
return;
}
const int selfIndex = m_parentItem->m_childItems.indexOf(this);
const int siblingIndex = m_parentItem->m_childItems.indexOf(sibling);
if (selfIndex == siblingIndex + 1) {
return;
}
m_parentItem->m_childItems.move(selfIndex, selfIndex > siblingIndex ? siblingIndex + 1 : siblingIndex);
scheduleRepaint(boundingRect());
sibling->scheduleRepaint(sibling->boundingRect());
discardQuads();
}
void Item::stackChildren(const QList<Item *> &children)
{
if (m_childItems.count() != children.count()) {
qCWarning(KWIN_CORE) << Q_FUNC_INFO << "invalid child list";
return;
}
#if !defined(QT_NO_DEBUG)
for (const Item *item : children) {
Q_ASSERT_X(item->parentItem() == this, Q_FUNC_INFO, "invalid parent");
}
#endif
m_childItems = children;
discardQuads();
}
void Item::scheduleRepaint(const QRegion &region)
{
window()->addLayerRepaint(mapToGlobal(region));
}
void Item::scheduleRepaint()
{
window()->scheduleRepaint();
}
void Item::preprocess()
{
}
void Item::discardQuads()
{
window()->discardQuads();
}
} // namespace KWin
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "scene.h"
namespace KWin
{
/**
* The Item class is the base class for items in the scene.
*/
class KWIN_EXPORT Item : public QObject
{
Q_OBJECT
public:
explicit Item(Scene::Window *window, Item *parent = nullptr);
~Item() override;
/**
* Returns the x coordinate relative to the top left corner of the parent item.
*/
int x() const;
void setX(int x);
/**