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 "composite.h"
|
|
|
|
#include "main.h"
|
2022-04-14 12:33:28 +00:00
|
|
|
#include "output.h"
|
2021-04-30 08:42:54 +00:00
|
|
|
#include "platform.h"
|
|
|
|
#include "renderloop.h"
|
2021-11-09 10:36:24 +00:00
|
|
|
#include "scene.h"
|
2021-04-30 08:42:54 +00:00
|
|
|
#include "screens.h"
|
2022-01-18 08:35:52 +00:00
|
|
|
#include "utils/common.h"
|
2021-02-04 09:07:20 +00:00
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
2021-08-12 09:07:38 +00:00
|
|
|
Item::Item(Item *parent)
|
2021-02-04 09:07:20 +00:00
|
|
|
{
|
|
|
|
setParentItem(parent);
|
2021-08-24 20:55:42 +00:00
|
|
|
connect(kwinApp()->platform(), &Platform::outputDisabled, this, &Item::removeRepaints);
|
2021-02-04 09:07:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Item::~Item()
|
|
|
|
{
|
|
|
|
setParentItem(nullptr);
|
2021-08-24 20:55:42 +00:00
|
|
|
for (const auto &dirty : qAsConst(m_repaints)) {
|
2021-04-30 08:42:54 +00:00
|
|
|
if (!dirty.isEmpty()) {
|
2021-11-09 10:36:24 +00:00
|
|
|
Compositor::self()->scene()->addRepaint(dirty);
|
2021-04-30 08:42:54 +00:00
|
|
|
}
|
|
|
|
}
|
2021-02-04 09:07:20 +00:00
|
|
|
}
|
|
|
|
|
2021-06-19 14:00:19 +00:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2021-02-04 09:07:20 +00:00
|
|
|
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);
|
|
|
|
}
|
2021-05-26 09:27:27 +00:00
|
|
|
updateEffectiveVisibility();
|
2021-02-04 09:07:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Item::addChild(Item *item)
|
|
|
|
{
|
|
|
|
Q_ASSERT(!m_childItems.contains(item));
|
|
|
|
|
|
|
|
m_childItems.append(item);
|
2021-06-19 14:00:19 +00:00
|
|
|
markSortedChildItemsDirty();
|
|
|
|
|
2021-02-04 09:07:20 +00:00
|
|
|
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);
|
2021-06-19 14:00:19 +00:00
|
|
|
markSortedChildItemsDirty();
|
2021-02-04 09:07:20 +00:00
|
|
|
|
|
|
|
updateBoundingRect();
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<Item *> Item::childItems() const
|
|
|
|
{
|
|
|
|
return m_childItems;
|
|
|
|
}
|
|
|
|
|
|
|
|
QPoint Item::position() const
|
|
|
|
{
|
2021-07-05 17:40:47 +00:00
|
|
|
return m_position;
|
2021-02-04 09:07:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Item::setPosition(const QPoint &point)
|
|
|
|
{
|
2021-07-05 17:40:47 +00:00
|
|
|
if (m_position != point) {
|
2021-02-04 09:07:20 +00:00
|
|
|
scheduleRepaint(boundingRect());
|
2021-07-05 17:40:47 +00:00
|
|
|
m_position = point;
|
2021-10-15 07:29:35 +00:00
|
|
|
if (m_parentItem) {
|
|
|
|
m_parentItem->updateBoundingRect();
|
|
|
|
}
|
2021-02-04 09:07:20 +00:00
|
|
|
scheduleRepaint(boundingRect());
|
2021-07-05 17:40:47 +00:00
|
|
|
Q_EMIT positionChanged();
|
2021-02-04 09:07:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QSize Item::size() const
|
|
|
|
{
|
2021-07-05 17:40:47 +00:00
|
|
|
return m_size;
|
2021-02-04 09:07:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Item::setSize(const QSize &size)
|
|
|
|
{
|
2021-07-05 17:40:47 +00:00
|
|
|
if (m_size != size) {
|
2021-02-04 09:07:20 +00:00
|
|
|
scheduleRepaint(rect());
|
2021-07-05 17:40:47 +00:00
|
|
|
m_size = size;
|
2021-02-04 09:07:20 +00:00
|
|
|
updateBoundingRect();
|
|
|
|
scheduleRepaint(rect());
|
|
|
|
discardQuads();
|
2021-07-05 17:40:47 +00:00
|
|
|
Q_EMIT sizeChanged();
|
2021-02-04 09:07:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT boundingRectChanged();
|
2021-07-05 17:32:03 +00:00
|
|
|
if (m_parentItem) {
|
|
|
|
m_parentItem->updateBoundingRect();
|
|
|
|
}
|
2021-02-04 09:07:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-07 10:20:11 +00:00
|
|
|
QRegion Item::shape() const
|
|
|
|
{
|
|
|
|
return rect();
|
|
|
|
}
|
|
|
|
|
|
|
|
QRegion Item::opaque() const
|
|
|
|
{
|
|
|
|
return QRegion();
|
|
|
|
}
|
|
|
|
|
2021-02-04 09:07:20 +00:00
|
|
|
QPoint Item::rootPosition() const
|
|
|
|
{
|
|
|
|
QPoint ret = position();
|
|
|
|
|
|
|
|
Item *parent = parentItem();
|
|
|
|
while (parent) {
|
|
|
|
ret += parent->position();
|
|
|
|
parent = parent->parentItem();
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-08-11 15:03:06 +00:00
|
|
|
QMatrix4x4 Item::transform() const
|
|
|
|
{
|
|
|
|
return m_transform;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Item::setTransform(const QMatrix4x4 &transform)
|
|
|
|
{
|
|
|
|
m_transform = transform;
|
|
|
|
}
|
|
|
|
|
2021-02-04 09:07:20 +00:00
|
|
|
QRegion Item::mapToGlobal(const QRegion ®ion) const
|
|
|
|
{
|
2022-05-07 10:20:11 +00:00
|
|
|
if (region.isEmpty()) {
|
|
|
|
return QRegion();
|
|
|
|
}
|
2021-02-04 09:07:20 +00:00
|
|
|
return region.translated(rootPosition());
|
|
|
|
}
|
|
|
|
|
|
|
|
QRect Item::mapToGlobal(const QRect &rect) const
|
|
|
|
{
|
2022-05-07 10:20:11 +00:00
|
|
|
if (rect.isEmpty()) {
|
|
|
|
return QRect();
|
|
|
|
}
|
2021-02-04 09:07:20 +00:00
|
|
|
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);
|
2021-06-19 14:00:19 +00:00
|
|
|
markSortedChildItemsDirty();
|
2021-02-04 09:07:20 +00:00
|
|
|
|
|
|
|
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);
|
2021-06-19 14:00:19 +00:00
|
|
|
markSortedChildItemsDirty();
|
2021-02-04 09:07:20 +00:00
|
|
|
|
|
|
|
scheduleRepaint(boundingRect());
|
|
|
|
sibling->scheduleRepaint(sibling->boundingRect());
|
|
|
|
}
|
|
|
|
|
|
|
|
void Item::scheduleRepaint(const QRegion ®ion)
|
2021-05-26 09:27:27 +00:00
|
|
|
{
|
|
|
|
if (isVisible()) {
|
|
|
|
scheduleRepaintInternal(region);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Item::scheduleRepaintInternal(const QRegion ®ion)
|
2021-02-04 09:07:20 +00:00
|
|
|
{
|
2022-04-14 12:33:28 +00:00
|
|
|
const QVector<Output *> outputs = kwinApp()->platform()->enabledOutputs();
|
2021-04-30 08:42:54 +00:00
|
|
|
const QRegion globalRegion = mapToGlobal(region);
|
2022-02-17 14:53:12 +00:00
|
|
|
if (kwinApp()->operationMode() != Application::OperationModeX11) {
|
2021-08-24 20:55:42 +00:00
|
|
|
for (const auto &output : outputs) {
|
2021-04-30 08:42:54 +00:00
|
|
|
const QRegion dirtyRegion = globalRegion & output->geometry();
|
|
|
|
if (!dirtyRegion.isEmpty()) {
|
2021-08-24 20:55:42 +00:00
|
|
|
m_repaints[output] += dirtyRegion;
|
2021-11-04 16:34:17 +00:00
|
|
|
output->renderLoop()->scheduleRepaint(this);
|
2021-04-30 08:42:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2022-02-05 13:20:17 +00:00
|
|
|
m_repaints[outputs.constFirst()] += globalRegion;
|
|
|
|
outputs.constFirst()->renderLoop()->scheduleRepaint(this);
|
2021-04-30 08:42:54 +00:00
|
|
|
}
|
2021-02-04 09:07:20 +00:00
|
|
|
}
|
|
|
|
|
2021-05-19 06:35:42 +00:00
|
|
|
void Item::scheduleFrame()
|
2021-02-04 09:07:20 +00:00
|
|
|
{
|
2021-05-26 09:27:27 +00:00
|
|
|
if (!isVisible()) {
|
|
|
|
return;
|
|
|
|
}
|
2022-04-14 12:33:28 +00:00
|
|
|
const QVector<Output *> outputs = kwinApp()->platform()->enabledOutputs();
|
2022-02-17 14:53:12 +00:00
|
|
|
if (kwinApp()->operationMode() != Application::OperationModeX11) {
|
2021-04-30 08:42:54 +00:00
|
|
|
const QRect geometry = mapToGlobal(rect());
|
2022-04-14 12:33:28 +00:00
|
|
|
for (const Output *output : outputs) {
|
2021-04-30 08:42:54 +00:00
|
|
|
if (output->geometry().intersects(geometry)) {
|
2021-11-04 16:34:17 +00:00
|
|
|
output->renderLoop()->scheduleRepaint(this);
|
2021-04-30 08:42:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2022-02-05 13:20:17 +00:00
|
|
|
outputs.constFirst()->renderLoop()->scheduleRepaint(this);
|
2021-04-30 08:42:54 +00:00
|
|
|
}
|
2021-02-04 09:07:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Item::preprocess()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-05-21 10:51:23 +00:00
|
|
|
WindowQuadList Item::buildQuads() const
|
|
|
|
{
|
|
|
|
return WindowQuadList();
|
|
|
|
}
|
|
|
|
|
2021-02-04 09:07:20 +00:00
|
|
|
void Item::discardQuads()
|
|
|
|
{
|
2021-06-19 17:50:19 +00:00
|
|
|
m_quads.reset();
|
2021-05-21 10:51:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
WindowQuadList Item::quads() const
|
|
|
|
{
|
2021-06-19 17:50:19 +00:00
|
|
|
if (!m_quads.has_value()) {
|
2021-05-21 10:51:23 +00:00
|
|
|
m_quads = buildQuads();
|
|
|
|
}
|
2021-06-19 17:50:19 +00:00
|
|
|
return m_quads.value();
|
2021-02-04 09:07:20 +00:00
|
|
|
}
|
|
|
|
|
2022-04-14 12:33:28 +00:00
|
|
|
QRegion Item::repaints(Output *output) const
|
2021-04-30 08:42:54 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
return m_repaints.value(output, QRect(QPoint(0, 0), screens()->size()));
|
2021-04-30 08:42:54 +00:00
|
|
|
}
|
|
|
|
|
2022-04-14 12:33:28 +00:00
|
|
|
void Item::resetRepaints(Output *output)
|
2021-04-30 08:42:54 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
m_repaints.insert(output, QRegion());
|
2021-04-30 08:42:54 +00:00
|
|
|
}
|
|
|
|
|
2022-04-14 12:33:28 +00:00
|
|
|
void Item::removeRepaints(Output *output)
|
2021-04-30 08:42:54 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
m_repaints.remove(output);
|
2021-04-30 08:42:54 +00:00
|
|
|
}
|
|
|
|
|
2022-04-27 11:07:21 +00:00
|
|
|
bool Item::explicitVisible() const
|
|
|
|
{
|
|
|
|
return m_explicitVisible;
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:27:27 +00:00
|
|
|
bool Item::isVisible() const
|
|
|
|
{
|
|
|
|
return m_effectiveVisible;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Item::setVisible(bool visible)
|
|
|
|
{
|
2022-04-27 11:07:21 +00:00
|
|
|
if (m_explicitVisible != visible) {
|
|
|
|
m_explicitVisible = visible;
|
2021-05-26 09:27:27 +00:00
|
|
|
updateEffectiveVisibility();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Item::computeEffectiveVisibility() const
|
|
|
|
{
|
2022-04-27 11:07:21 +00:00
|
|
|
return m_explicitVisible && (!m_parentItem || m_parentItem->isVisible());
|
2021-05-26 09:27:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Item::updateEffectiveVisibility()
|
|
|
|
{
|
|
|
|
const bool effectiveVisible = computeEffectiveVisibility();
|
|
|
|
if (m_effectiveVisible == effectiveVisible) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_effectiveVisible = effectiveVisible;
|
2022-04-27 10:52:30 +00:00
|
|
|
if (!m_effectiveVisible) {
|
|
|
|
Compositor::self()->scene()->addRepaint(mapToGlobal(boundingRect()));
|
|
|
|
} else {
|
|
|
|
scheduleRepaintInternal(boundingRect());
|
|
|
|
}
|
2021-05-26 09:27:27 +00:00
|
|
|
|
|
|
|
for (Item *childItem : qAsConst(m_childItems)) {
|
|
|
|
childItem->updateEffectiveVisibility();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-19 14:00:19 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2021-02-04 09:07:20 +00:00
|
|
|
} // namespace KWin
|