8e7a8c5a11
AbstractOutput is not so Abstract and it's common to avoid the word "Abstract" in class names as it doesn't contribute any new information. It also significantly reduces the line width in some places.
382 lines
8.3 KiB
C++
382 lines
8.3 KiB
C++
/*
|
|
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "item.h"
|
|
#include "composite.h"
|
|
#include "main.h"
|
|
#include "output.h"
|
|
#include "platform.h"
|
|
#include "renderloop.h"
|
|
#include "scene.h"
|
|
#include "screens.h"
|
|
#include "utils/common.h"
|
|
|
|
namespace KWin
|
|
{
|
|
|
|
Item::Item(Item *parent)
|
|
{
|
|
setParentItem(parent);
|
|
connect(kwinApp()->platform(), &Platform::outputDisabled, this, &Item::removeRepaints);
|
|
}
|
|
|
|
Item::~Item()
|
|
{
|
|
setParentItem(nullptr);
|
|
for (const auto &dirty : qAsConst(m_repaints)) {
|
|
if (!dirty.isEmpty()) {
|
|
Compositor::self()->scene()->addRepaint(dirty);
|
|
}
|
|
}
|
|
}
|
|
|
|
int Item::z() const
|
|
{
|
|
return m_z;
|
|
}
|
|
|
|
void Item::setZ(int z)
|
|
{
|
|
if (m_z == z) {
|
|
return;
|
|
}
|
|
m_z = z;
|
|
if (m_parentItem) {
|
|
m_parentItem->markSortedChildItemsDirty();
|
|
}
|
|
scheduleRepaint(boundingRect());
|
|
}
|
|
|
|
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);
|
|
}
|
|
updateEffectiveVisibility();
|
|
}
|
|
|
|
void Item::addChild(Item *item)
|
|
{
|
|
Q_ASSERT(!m_childItems.contains(item));
|
|
|
|
m_childItems.append(item);
|
|
markSortedChildItemsDirty();
|
|
|
|
updateBoundingRect();
|
|
scheduleRepaint(item->boundingRect().translated(item->position()));
|
|
}
|
|
|
|
void Item::removeChild(Item *item)
|
|
{
|
|
Q_ASSERT(m_childItems.contains(item));
|
|
scheduleRepaint(item->boundingRect().translated(item->position()));
|
|
|
|
m_childItems.removeOne(item);
|
|
markSortedChildItemsDirty();
|
|
|
|
updateBoundingRect();
|
|
}
|
|
|
|
QList<Item *> Item::childItems() const
|
|
{
|
|
return m_childItems;
|
|
}
|
|
|
|
QPoint Item::position() const
|
|
{
|
|
return m_position;
|
|
}
|
|
|
|
void Item::setPosition(const QPoint &point)
|
|
{
|
|
if (m_position != point) {
|
|
scheduleRepaint(boundingRect());
|
|
m_position = point;
|
|
if (m_parentItem) {
|
|
m_parentItem->updateBoundingRect();
|
|
}
|
|
scheduleRepaint(boundingRect());
|
|
Q_EMIT positionChanged();
|
|
}
|
|
}
|
|
|
|
QSize Item::size() const
|
|
{
|
|
return m_size;
|
|
}
|
|
|
|
void Item::setSize(const QSize &size)
|
|
{
|
|
if (m_size != size) {
|
|
scheduleRepaint(rect());
|
|
m_size = size;
|
|
updateBoundingRect();
|
|
scheduleRepaint(rect());
|
|
discardQuads();
|
|
Q_EMIT sizeChanged();
|
|
}
|
|
}
|
|
|
|
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;
|
|
Q_EMIT boundingRectChanged();
|
|
if (m_parentItem) {
|
|
m_parentItem->updateBoundingRect();
|
|
}
|
|
}
|
|
}
|
|
|
|
QPoint Item::rootPosition() const
|
|
{
|
|
QPoint ret = position();
|
|
|
|
Item *parent = parentItem();
|
|
while (parent) {
|
|
ret += parent->position();
|
|
parent = parent->parentItem();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QMatrix4x4 Item::transform() const
|
|
{
|
|
return m_transform;
|
|
}
|
|
|
|
void Item::setTransform(const QMatrix4x4 &transform)
|
|
{
|
|
m_transform = transform;
|
|
}
|
|
|
|
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);
|
|
markSortedChildItemsDirty();
|
|
|
|
scheduleRepaint(boundingRect());
|
|
sibling->scheduleRepaint(sibling->boundingRect());
|
|
}
|
|
|
|
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);
|
|
markSortedChildItemsDirty();
|
|
|
|
scheduleRepaint(boundingRect());
|
|
sibling->scheduleRepaint(sibling->boundingRect());
|
|
}
|
|
|
|
void Item::scheduleRepaint(const QRegion ®ion)
|
|
{
|
|
if (isVisible()) {
|
|
scheduleRepaintInternal(region);
|
|
}
|
|
}
|
|
|
|
void Item::scheduleRepaintInternal(const QRegion ®ion)
|
|
{
|
|
const QVector<Output *> outputs = kwinApp()->platform()->enabledOutputs();
|
|
const QRegion globalRegion = mapToGlobal(region);
|
|
if (kwinApp()->operationMode() != Application::OperationModeX11) {
|
|
for (const auto &output : outputs) {
|
|
const QRegion dirtyRegion = globalRegion & output->geometry();
|
|
if (!dirtyRegion.isEmpty()) {
|
|
m_repaints[output] += dirtyRegion;
|
|
output->renderLoop()->scheduleRepaint(this);
|
|
}
|
|
}
|
|
} else {
|
|
m_repaints[outputs.constFirst()] += globalRegion;
|
|
outputs.constFirst()->renderLoop()->scheduleRepaint(this);
|
|
}
|
|
}
|
|
|
|
void Item::scheduleFrame()
|
|
{
|
|
if (!isVisible()) {
|
|
return;
|
|
}
|
|
const QVector<Output *> outputs = kwinApp()->platform()->enabledOutputs();
|
|
if (kwinApp()->operationMode() != Application::OperationModeX11) {
|
|
const QRect geometry = mapToGlobal(rect());
|
|
for (const Output *output : outputs) {
|
|
if (output->geometry().intersects(geometry)) {
|
|
output->renderLoop()->scheduleRepaint(this);
|
|
}
|
|
}
|
|
} else {
|
|
outputs.constFirst()->renderLoop()->scheduleRepaint(this);
|
|
}
|
|
}
|
|
|
|
void Item::preprocess()
|
|
{
|
|
}
|
|
|
|
WindowQuadList Item::buildQuads() const
|
|
{
|
|
return WindowQuadList();
|
|
}
|
|
|
|
void Item::discardQuads()
|
|
{
|
|
m_quads.reset();
|
|
}
|
|
|
|
WindowQuadList Item::quads() const
|
|
{
|
|
if (!m_quads.has_value()) {
|
|
m_quads = buildQuads();
|
|
}
|
|
return m_quads.value();
|
|
}
|
|
|
|
QRegion Item::repaints(Output *output) const
|
|
{
|
|
return m_repaints.value(output, QRect(QPoint(0, 0), screens()->size()));
|
|
}
|
|
|
|
void Item::resetRepaints(Output *output)
|
|
{
|
|
m_repaints.insert(output, QRegion());
|
|
}
|
|
|
|
void Item::removeRepaints(Output *output)
|
|
{
|
|
m_repaints.remove(output);
|
|
}
|
|
|
|
bool Item::isVisible() const
|
|
{
|
|
return m_effectiveVisible;
|
|
}
|
|
|
|
void Item::setVisible(bool visible)
|
|
{
|
|
if (m_visible != visible) {
|
|
m_visible = visible;
|
|
updateEffectiveVisibility();
|
|
}
|
|
}
|
|
|
|
bool Item::computeEffectiveVisibility() const
|
|
{
|
|
return m_visible && (!m_parentItem || m_parentItem->isVisible());
|
|
}
|
|
|
|
void Item::updateEffectiveVisibility()
|
|
{
|
|
const bool effectiveVisible = computeEffectiveVisibility();
|
|
if (m_effectiveVisible == effectiveVisible) {
|
|
return;
|
|
}
|
|
|
|
m_effectiveVisible = effectiveVisible;
|
|
scheduleRepaintInternal(boundingRect());
|
|
|
|
for (Item *childItem : qAsConst(m_childItems)) {
|
|
childItem->updateEffectiveVisibility();
|
|
}
|
|
}
|
|
|
|
static bool compareZ(const Item *a, const Item *b)
|
|
{
|
|
return a->z() < b->z();
|
|
}
|
|
|
|
QList<Item *> Item::sortedChildItems() const
|
|
{
|
|
if (!m_sortedChildItems.has_value()) {
|
|
QList<Item *> items = m_childItems;
|
|
std::stable_sort(items.begin(), items.end(), compareZ);
|
|
m_sortedChildItems = items;
|
|
}
|
|
return m_sortedChildItems.value();
|
|
}
|
|
|
|
void Item::markSortedChildItemsDirty()
|
|
{
|
|
m_sortedChildItems.reset();
|
|
}
|
|
|
|
} // namespace KWin
|