You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
435 lines
17 KiB
435 lines
17 KiB
/******************************************************************** |
|
KWin - the KDE window manager |
|
This file is part of the KDE project. |
|
|
|
Copyright (C) 2011 Martin Gräßlin <mgraesslin@kde.org> |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation; either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
*********************************************************************/ |
|
#include "shadow.h" |
|
// kwin |
|
#include "atoms.h" |
|
#include "client.h" |
|
#include "composite.h" |
|
#include "effects.h" |
|
#include "toplevel.h" |
|
#include "wayland_server.h" |
|
|
|
#include <KDecoration2/Decoration> |
|
#include <KDecoration2/DecorationShadow> |
|
|
|
#include <KWayland/Server/buffer_interface.h> |
|
#include <KWayland/Server/shadow_interface.h> |
|
#include <KWayland/Server/surface_interface.h> |
|
|
|
namespace KWin |
|
{ |
|
|
|
Shadow::Shadow(Toplevel *toplevel) |
|
: m_topLevel(toplevel) |
|
, m_cachedSize(toplevel->geometry().size()) |
|
, m_decorationShadow(nullptr) |
|
{ |
|
connect(m_topLevel, SIGNAL(geometryChanged()), SLOT(geometryChanged())); |
|
} |
|
|
|
Shadow::~Shadow() |
|
{ |
|
} |
|
|
|
Shadow *Shadow::createShadow(Toplevel *toplevel) |
|
{ |
|
if (!effects) { |
|
return NULL; |
|
} |
|
Shadow *shadow = crateShadowFromDecoration(toplevel); |
|
if (!shadow && waylandServer()) { |
|
shadow = createShadowFromWayland(toplevel); |
|
} |
|
if (!shadow) { |
|
shadow = createShadowFromX11(toplevel); |
|
} |
|
if (shadow) { |
|
if (toplevel->effectWindow() && toplevel->effectWindow()->sceneWindow()) { |
|
toplevel->effectWindow()->sceneWindow()->updateShadow(shadow); |
|
} |
|
if (shadow->hasDecorationShadow()) { |
|
if (toplevel->effectWindow()) { |
|
toplevel->effectWindow()->buildQuads(true); |
|
} |
|
} |
|
} |
|
return shadow; |
|
} |
|
|
|
Shadow *Shadow::createShadowFromX11(Toplevel *toplevel) |
|
{ |
|
auto data = Shadow::readX11ShadowProperty(toplevel->window()); |
|
if (!data.isEmpty()) { |
|
Shadow *shadow = Compositor::self()->scene()->createShadow(toplevel); |
|
|
|
if (!shadow->init(data)) { |
|
delete shadow; |
|
return NULL; |
|
} |
|
return shadow; |
|
} else { |
|
return NULL; |
|
} |
|
} |
|
|
|
Shadow *Shadow::crateShadowFromDecoration(Toplevel *toplevel) |
|
{ |
|
Client *c = qobject_cast<Client*>(toplevel); |
|
if (!c) { |
|
return nullptr; |
|
} |
|
if (!c->decoration()) { |
|
return nullptr; |
|
} |
|
Shadow *shadow = Compositor::self()->scene()->createShadow(toplevel); |
|
if (!shadow->init(c->decoration())) { |
|
delete shadow; |
|
return nullptr; |
|
} |
|
return shadow; |
|
} |
|
|
|
Shadow *Shadow::createShadowFromWayland(Toplevel *toplevel) |
|
{ |
|
auto surface = toplevel->surface(); |
|
if (!surface) { |
|
return nullptr; |
|
} |
|
const auto s = surface->shadow(); |
|
if (!s) { |
|
return nullptr; |
|
} |
|
Shadow *shadow = Compositor::self()->scene()->createShadow(toplevel); |
|
if (!shadow->init(s)) { |
|
delete shadow; |
|
return nullptr; |
|
} |
|
return shadow; |
|
} |
|
|
|
QVector< uint32_t > Shadow::readX11ShadowProperty(xcb_window_t id) |
|
{ |
|
QVector<uint32_t> ret; |
|
Xcb::Property property(false, id, atoms->kde_net_wm_shadow, XCB_ATOM_CARDINAL, 0, 12); |
|
uint32_t *shadow = property.value<uint32_t*>(); |
|
if (shadow) { |
|
ret.reserve(12); |
|
for (int i=0; i<12; ++i) { |
|
ret << shadow[i]; |
|
} |
|
} |
|
return ret; |
|
} |
|
|
|
bool Shadow::init(const QVector< uint32_t > &data) |
|
{ |
|
QVector<Xcb::WindowGeometry> pixmapGeometries(ShadowElementsCount); |
|
QVector<xcb_get_image_cookie_t> getImageCookies(ShadowElementsCount); |
|
auto *c = connection(); |
|
for (int i = 0; i < ShadowElementsCount; ++i) { |
|
pixmapGeometries[i] = Xcb::WindowGeometry(data[i]); |
|
} |
|
auto discardReplies = [&getImageCookies](int start) { |
|
for (int i = start; i < getImageCookies.size(); ++i) { |
|
xcb_discard_reply(connection(), getImageCookies.at(i).sequence); |
|
} |
|
}; |
|
for (int i = 0; i < ShadowElementsCount; ++i) { |
|
auto &geo = pixmapGeometries[i]; |
|
if (geo.isNull()) { |
|
discardReplies(0); |
|
return false; |
|
} |
|
getImageCookies[i] = xcb_get_image_unchecked(c, XCB_IMAGE_FORMAT_Z_PIXMAP, data[i], |
|
0, 0, geo->width, geo->height, ~0); |
|
} |
|
for (int i = 0; i < ShadowElementsCount; ++i) { |
|
auto *reply = xcb_get_image_reply(c, getImageCookies.at(i), nullptr); |
|
if (!reply) { |
|
discardReplies(i+1); |
|
return false; |
|
} |
|
auto &geo = pixmapGeometries[i]; |
|
QImage image(xcb_get_image_data(reply), geo->width, geo->height, QImage::Format_ARGB32); |
|
m_shadowElements[i] = QPixmap::fromImage(image); |
|
free(reply); |
|
} |
|
m_topOffset = data[ShadowElementsCount]; |
|
m_rightOffset = data[ShadowElementsCount+1]; |
|
m_bottomOffset = data[ShadowElementsCount+2]; |
|
m_leftOffset = data[ShadowElementsCount+3]; |
|
updateShadowRegion(); |
|
if (!prepareBackend()) { |
|
return false; |
|
} |
|
buildQuads(); |
|
return true; |
|
} |
|
|
|
bool Shadow::init(KDecoration2::Decoration *decoration) |
|
{ |
|
if (m_decorationShadow) { |
|
// disconnect previous connections |
|
disconnect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::innerShadowRectChanged, m_topLevel, &Toplevel::getShadow); |
|
disconnect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::shadowChanged, m_topLevel, &Toplevel::getShadow); |
|
disconnect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::paddingChanged, m_topLevel, &Toplevel::getShadow); |
|
} |
|
m_decorationShadow = decoration->shadow(); |
|
if (!m_decorationShadow) { |
|
return false; |
|
} |
|
// setup connections - all just mapped to recreate |
|
connect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::innerShadowRectChanged, m_topLevel, &Toplevel::getShadow); |
|
connect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::shadowChanged, m_topLevel, &Toplevel::getShadow); |
|
connect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::paddingChanged, m_topLevel, &Toplevel::getShadow); |
|
|
|
const QMargins &p = m_decorationShadow->padding(); |
|
m_topOffset = p.top(); |
|
m_rightOffset = p.right(); |
|
m_bottomOffset = p.bottom(); |
|
m_leftOffset = p.left(); |
|
updateShadowRegion(); |
|
if (!prepareBackend()) { |
|
return false; |
|
} |
|
buildQuads(); |
|
return true; |
|
} |
|
|
|
bool Shadow::init(const QPointer< KWayland::Server::ShadowInterface > &shadow) |
|
{ |
|
if (!shadow) { |
|
return false; |
|
} |
|
|
|
m_shadowElements[ShadowElementTop] = shadow->top() ? QPixmap::fromImage(shadow->top()->data().copy()) : QPixmap(); |
|
m_shadowElements[ShadowElementTopRight] = shadow->topRight() ? QPixmap::fromImage(shadow->topRight()->data().copy()) : QPixmap(); |
|
m_shadowElements[ShadowElementRight] = shadow->right() ? QPixmap::fromImage(shadow->right()->data().copy()) : QPixmap(); |
|
m_shadowElements[ShadowElementBottomRight] = shadow->bottomRight() ? QPixmap::fromImage(shadow->bottomRight()->data().copy()) : QPixmap(); |
|
m_shadowElements[ShadowElementBottom] = shadow->bottom() ? QPixmap::fromImage(shadow->bottom()->data().copy()) : QPixmap(); |
|
m_shadowElements[ShadowElementBottomLeft] = shadow->bottomLeft() ? QPixmap::fromImage(shadow->bottomLeft()->data().copy()) : QPixmap(); |
|
m_shadowElements[ShadowElementLeft] = shadow->left() ? QPixmap::fromImage(shadow->left()->data().copy()) : QPixmap(); |
|
m_shadowElements[ShadowElementTopLeft] = shadow->topLeft() ? QPixmap::fromImage(shadow->topLeft()->data().copy()) : QPixmap(); |
|
|
|
const QMarginsF &p = shadow->offset(); |
|
m_topOffset = p.top(); |
|
m_rightOffset = p.right(); |
|
m_bottomOffset = p.bottom(); |
|
m_leftOffset = p.left(); |
|
updateShadowRegion(); |
|
if (!prepareBackend()) { |
|
return false; |
|
} |
|
buildQuads(); |
|
return true; |
|
} |
|
|
|
void Shadow::updateShadowRegion() |
|
{ |
|
const QRect top(0, - m_topOffset, m_topLevel->width(), m_topOffset); |
|
const QRect right(m_topLevel->width(), - m_topOffset, m_rightOffset, m_topLevel->height() + m_topOffset + m_bottomOffset); |
|
const QRect bottom(0, m_topLevel->height(), m_topLevel->width(), m_bottomOffset); |
|
const QRect left(- m_leftOffset, - m_topOffset, m_leftOffset, m_topLevel->height() + m_topOffset + m_bottomOffset); |
|
m_shadowRegion = QRegion(top).united(right).united(bottom).united(left); |
|
} |
|
|
|
void Shadow::buildQuads() |
|
{ |
|
// prepare window quads |
|
m_shadowQuads.clear(); |
|
const QSize top(m_shadowElements[ShadowElementTop].size()); |
|
const QSize topRight(m_shadowElements[ShadowElementTopRight].size()); |
|
const QSize right(m_shadowElements[ShadowElementRight].size()); |
|
const QSize bottomRight(m_shadowElements[ShadowElementBottomRight].size()); |
|
const QSize bottom(m_shadowElements[ShadowElementBottom].size()); |
|
const QSize bottomLeft(m_shadowElements[ShadowElementBottomLeft].size()); |
|
const QSize left(m_shadowElements[ShadowElementLeft].size()); |
|
const QSize topLeft(m_shadowElements[ShadowElementTopLeft].size()); |
|
if ((left.width() - m_leftOffset > m_topLevel->width()) || |
|
(right.width() - m_rightOffset > m_topLevel->width()) || |
|
(top.height() - m_topOffset > m_topLevel->height()) || |
|
(bottom.height() - m_bottomOffset > m_topLevel->height())) { |
|
// if our shadow is bigger than the window, we don't render the shadow |
|
m_shadowRegion = QRegion(); |
|
return; |
|
} |
|
|
|
const QRect outerRect(QPoint(-m_leftOffset, -m_topOffset), QPoint(m_topLevel->width() + m_rightOffset, m_topLevel->height() + m_bottomOffset)); |
|
|
|
WindowQuad topLeftQuad(WindowQuadShadowTopLeft); |
|
topLeftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.y(), 0.0, 0.0); |
|
topLeftQuad[ 1 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y(), 1.0, 0.0); |
|
topLeftQuad[ 2 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y() + topLeft.height(), 1.0, 1.0); |
|
topLeftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.y() + topLeft.height(), 0.0, 1.0); |
|
m_shadowQuads.append(topLeftQuad); |
|
|
|
WindowQuad topQuad(WindowQuadShadowTop); |
|
topQuad[ 0 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y(), 0.0, 0.0); |
|
topQuad[ 1 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y(), 1.0, 0.0); |
|
topQuad[ 2 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y() + top.height(), 1.0, 1.0); |
|
topQuad[ 3 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y() + top.height(), 0.0, 1.0); |
|
m_shadowQuads.append(topQuad); |
|
|
|
WindowQuad topRightQuad(WindowQuadShadowTopRight); |
|
topRightQuad[ 0 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y(), 0.0, 0.0); |
|
topRightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.y(), 1.0, 0.0); |
|
topRightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.y() + topRight.height(), 1.0, 1.0); |
|
topRightQuad[ 3 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y() + topRight.height(), 0.0, 1.0); |
|
m_shadowQuads.append(topRightQuad); |
|
|
|
WindowQuad rightQuad(WindowQuadShadowRight); |
|
rightQuad[ 0 ] = WindowVertex(outerRect.right() - right.width(), outerRect.y() + topRight.height(), 0.0, 0.0); |
|
rightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.y() + topRight.height(), 1.0, 0.0); |
|
rightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.bottom() - bottomRight.height(), 1.0, 1.0); |
|
rightQuad[ 3 ] = WindowVertex(outerRect.right() - right.width(), outerRect.bottom() - bottomRight.height(), 0.0, 1.0); |
|
m_shadowQuads.append(rightQuad); |
|
|
|
WindowQuad bottomRightQuad(WindowQuadShadowBottomRight); |
|
bottomRightQuad[ 0 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom() - bottomRight.height(), 0.0, 0.0); |
|
bottomRightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.bottom() - bottomRight.height(), 1.0, 0.0); |
|
bottomRightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.bottom(), 1.0, 1.0); |
|
bottomRightQuad[ 3 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom(), 0.0, 1.0); |
|
m_shadowQuads.append(bottomRightQuad); |
|
|
|
WindowQuad bottomQuad(WindowQuadShadowBottom); |
|
bottomQuad[ 0 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom() - bottom.height(), 0.0, 0.0); |
|
bottomQuad[ 1 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom() - bottom.height(), 1.0, 0.0); |
|
bottomQuad[ 2 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom(), 1.0, 1.0); |
|
bottomQuad[ 3 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom(), 0.0, 1.0); |
|
m_shadowQuads.append(bottomQuad); |
|
|
|
WindowQuad bottomLeftQuad(WindowQuadShadowBottomLeft); |
|
bottomLeftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.bottom() - bottomLeft.height(), 0.0, 0.0); |
|
bottomLeftQuad[ 1 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom() - bottomLeft.height(), 1.0, 0.0); |
|
bottomLeftQuad[ 2 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom(), 1.0, 1.0); |
|
bottomLeftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.bottom(), 0.0, 1.0); |
|
m_shadowQuads.append(bottomLeftQuad); |
|
|
|
WindowQuad leftQuad(WindowQuadShadowLeft); |
|
leftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.y() + topLeft.height(), 0.0, 0.0); |
|
leftQuad[ 1 ] = WindowVertex(outerRect.x() + left.width(), outerRect.y() + topLeft.height(), 1.0, 0.0); |
|
leftQuad[ 2 ] = WindowVertex(outerRect.x() + left.width(), outerRect.bottom() - bottomLeft.height(), 1.0, 1.0); |
|
leftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.bottom() - bottomLeft.height(), 0.0, 1.0); |
|
m_shadowQuads.append(leftQuad); |
|
} |
|
|
|
bool Shadow::updateShadow() |
|
{ |
|
auto clear = [this]() { |
|
if (m_topLevel && m_topLevel->effectWindow() && m_topLevel->effectWindow()->sceneWindow() && |
|
m_topLevel->effectWindow()->sceneWindow()->shadow()) { |
|
m_topLevel->effectWindow()->sceneWindow()->updateShadow(0); |
|
m_topLevel->effectWindow()->buildQuads(true); |
|
} |
|
deleteLater(); |
|
}; |
|
if (m_decorationShadow) { |
|
if (Client *c = qobject_cast<Client*>(m_topLevel)) { |
|
if (c->decoration()) { |
|
if (init(c->decoration())) { |
|
if (m_topLevel && m_topLevel->effectWindow()) |
|
m_topLevel->effectWindow()->buildQuads(true); |
|
return true; |
|
} |
|
} |
|
} |
|
clear(); |
|
return false; |
|
} |
|
if (waylandServer()) { |
|
if (m_topLevel && m_topLevel->surface()) { |
|
if (const auto &s = m_topLevel->surface()->shadow()) { |
|
if (init(s)) { |
|
if (m_topLevel->effectWindow()) { |
|
m_topLevel->effectWindow()->buildQuads(true); |
|
} |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
auto data = Shadow::readX11ShadowProperty(m_topLevel->window()); |
|
if (data.isEmpty()) { |
|
clear(); |
|
return false; |
|
} |
|
init(data); |
|
if (m_topLevel && m_topLevel->effectWindow()) |
|
m_topLevel->effectWindow()->buildQuads(true); |
|
return true; |
|
} |
|
|
|
void Shadow::setToplevel(Toplevel *topLevel) |
|
{ |
|
m_topLevel = topLevel; |
|
connect(m_topLevel, SIGNAL(geometryChanged()), SLOT(geometryChanged())); |
|
} |
|
void Shadow::geometryChanged() |
|
{ |
|
if (m_cachedSize == m_topLevel->geometry().size()) { |
|
return; |
|
} |
|
m_cachedSize = m_topLevel->geometry().size(); |
|
updateShadowRegion(); |
|
buildQuads(); |
|
} |
|
|
|
QImage Shadow::decorationShadowImage() const |
|
{ |
|
if (!m_decorationShadow) { |
|
return QImage(); |
|
} |
|
return m_decorationShadow->shadow(); |
|
} |
|
|
|
QSize Shadow::elementSize(Shadow::ShadowElements element) const |
|
{ |
|
if (m_decorationShadow) { |
|
switch (element) { |
|
case ShadowElementTop: |
|
return m_decorationShadow->topGeometry().size(); |
|
case ShadowElementTopRight: |
|
return m_decorationShadow->topRightGeometry().size(); |
|
case ShadowElementRight: |
|
return m_decorationShadow->rightGeometry().size(); |
|
case ShadowElementBottomRight: |
|
return m_decorationShadow->bottomRightGeometry().size(); |
|
case ShadowElementBottom: |
|
return m_decorationShadow->bottomGeometry().size(); |
|
case ShadowElementBottomLeft: |
|
return m_decorationShadow->bottomLeftGeometry().size(); |
|
case ShadowElementLeft: |
|
return m_decorationShadow->leftGeometry().size(); |
|
case ShadowElementTopLeft: |
|
return m_decorationShadow->topLeftGeometry().size(); |
|
default: |
|
return QSize(); |
|
} |
|
} else { |
|
return m_shadowElements[element].size(); |
|
} |
|
} |
|
|
|
void Shadow::setShadowElement(const QPixmap &shadow, Shadow::ShadowElements element) |
|
{ |
|
m_shadowElements[element] = shadow; |
|
} |
|
|
|
} // namespace
|
|
|