2021-02-04 09:07:20 +00:00
|
|
|
/*
|
|
|
|
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
|
|
|
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "item.h"
|
2021-04-30 08:42:54 +00:00
|
|
|
#include "abstract_output.h"
|
|
|
|
#include "composite.h"
|
|
|
|
#include "main.h"
|
|
|
|
#include "platform.h"
|
|
|
|
#include "renderloop.h"
|
|
|
|
#include "screens.h"
|
2021-02-04 09:07:20 +00:00
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
|
|
|
Item::Item(Scene::Window *window, Item *parent)
|
|
|
|
: m_window(window)
|
|
|
|
{
|
|
|
|
setParentItem(parent);
|
2021-04-30 08:42:54 +00:00
|
|
|
|
|
|
|
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
|
|
|
|
connect(kwinApp()->platform(), &Platform::outputEnabled, this, &Item::reallocRepaints);
|
|
|
|
connect(kwinApp()->platform(), &Platform::outputDisabled, this, &Item::reallocRepaints);
|
|
|
|
}
|
|
|
|
reallocRepaints();
|
2021-02-04 09:07:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Item::~Item()
|
|
|
|
{
|
|
|
|
setParentItem(nullptr);
|
2021-04-30 08:42:54 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < m_repaints.count(); ++i) {
|
|
|
|
const QRegion dirty = repaints(i);
|
|
|
|
if (!dirty.isEmpty()) {
|
|
|
|
Compositor::self()->addRepaint(dirty);
|
|
|
|
}
|
|
|
|
}
|
2021-02-04 09:07:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 ®ion) 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 ®ion)
|
|
|
|
{
|
2021-04-30 08:42:54 +00:00
|
|
|
const QRegion globalRegion = mapToGlobal(region);
|
|
|
|
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
|
|
|
|
const QVector<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs();
|
|
|
|
if (m_repaints.count() != outputs.count()) {
|
|
|
|
return; // Repaints haven't been reallocated yet, do nothing.
|
|
|
|
}
|
|
|
|
for (int screenId = 0; screenId < m_repaints.count(); ++screenId) {
|
|
|
|
AbstractOutput *output = outputs[screenId];
|
|
|
|
const QRegion dirtyRegion = globalRegion & output->geometry();
|
|
|
|
if (!dirtyRegion.isEmpty()) {
|
|
|
|
m_repaints[screenId] += dirtyRegion;
|
|
|
|
output->renderLoop()->scheduleRepaint();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
m_repaints[0] += globalRegion;
|
|
|
|
kwinApp()->platform()->renderLoop()->scheduleRepaint();
|
|
|
|
}
|
2021-02-04 09:07:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Item::scheduleRepaint()
|
|
|
|
{
|
2021-04-30 08:42:54 +00:00
|
|
|
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
|
|
|
|
const QRect geometry = mapToGlobal(rect());
|
|
|
|
const QVector<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs();
|
|
|
|
for (const AbstractOutput *output : outputs) {
|
|
|
|
if (output->geometry().intersects(geometry)) {
|
|
|
|
output->renderLoop()->scheduleRepaint();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
kwinApp()->platform()->renderLoop()->scheduleRepaint();
|
|
|
|
}
|
2021-02-04 09:07:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Item::preprocess()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Item::discardQuads()
|
|
|
|
{
|
|
|
|
window()->discardQuads();
|
|
|
|
}
|
|
|
|
|
2021-04-30 08:42:54 +00:00
|
|
|
QRegion Item::repaints(int screen) const
|
|
|
|
{
|
|
|
|
Q_ASSERT(!m_repaints.isEmpty());
|
|
|
|
const int index = screen != -1 ? screen : 0;
|
|
|
|
if (m_repaints[index] == infiniteRegion()) {
|
|
|
|
return QRect(QPoint(0, 0), screens()->size());
|
|
|
|
}
|
|
|
|
return m_repaints[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Item::resetRepaints(int screen)
|
|
|
|
{
|
|
|
|
Q_ASSERT(!m_repaints.isEmpty());
|
|
|
|
const int index = screen != -1 ? screen : 0;
|
|
|
|
m_repaints[index] = QRegion();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Item::reallocRepaints()
|
|
|
|
{
|
|
|
|
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
|
|
|
|
m_repaints.resize(kwinApp()->platform()->enabledOutputs().count());
|
|
|
|
} else {
|
|
|
|
m_repaints.resize(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_repaints.fill(infiniteRegion());
|
|
|
|
}
|
|
|
|
|
2021-02-04 09:07:20 +00:00
|
|
|
} // namespace KWin
|