2011-03-27 10:33:07 +00:00
|
|
|
/********************************************************************
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
|
|
|
|
|
|
|
Copyright (C) 2011 Martin Gräßlin <mgraesslin@kde.org>
|
2020-01-01 01:11:17 +00:00
|
|
|
Copyright (C) 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
2011-03-27 10:33:07 +00:00
|
|
|
|
|
|
|
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"
|
2015-12-04 11:27:00 +00:00
|
|
|
#include "abstract_client.h"
|
2013-06-24 07:06:50 +00:00
|
|
|
#include "composite.h"
|
2011-04-03 09:31:33 +00:00
|
|
|
#include "effects.h"
|
2020-01-01 01:11:17 +00:00
|
|
|
#include "internal_client.h"
|
2011-03-27 10:33:07 +00:00
|
|
|
#include "toplevel.h"
|
2015-07-15 09:24:19 +00:00
|
|
|
#include "wayland_server.h"
|
2011-03-27 10:33:07 +00:00
|
|
|
|
2014-07-24 06:39:25 +00:00
|
|
|
#include <KDecoration2/Decoration>
|
|
|
|
#include <KDecoration2/DecorationShadow>
|
|
|
|
|
2015-07-15 09:24:19 +00:00
|
|
|
#include <KWayland/Server/buffer_interface.h>
|
|
|
|
#include <KWayland/Server/shadow_interface.h>
|
|
|
|
#include <KWayland/Server/surface_interface.h>
|
|
|
|
|
2020-01-01 01:11:17 +00:00
|
|
|
#include <QWindow>
|
|
|
|
|
|
|
|
Q_DECLARE_METATYPE(QMargins)
|
|
|
|
|
2011-03-27 10:33:07 +00:00
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
|
|
|
Shadow::Shadow(Toplevel *toplevel)
|
2011-04-03 09:31:33 +00:00
|
|
|
: m_topLevel(toplevel)
|
2019-09-27 10:01:10 +00:00
|
|
|
, m_cachedSize(toplevel->size())
|
2014-07-24 06:39:25 +00:00
|
|
|
, m_decorationShadow(nullptr)
|
2011-03-27 10:33:07 +00:00
|
|
|
{
|
2020-02-05 09:28:50 +00:00
|
|
|
connect(m_topLevel, &Toplevel::frameGeometryChanged, this, &Shadow::geometryChanged);
|
2011-03-27 10:33:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Shadow::~Shadow()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Shadow *Shadow::createShadow(Toplevel *toplevel)
|
|
|
|
{
|
|
|
|
if (!effects) {
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
return nullptr;
|
2011-03-27 10:33:07 +00:00
|
|
|
}
|
2018-08-03 17:35:22 +00:00
|
|
|
Shadow *shadow = createShadowFromDecoration(toplevel);
|
2015-07-15 09:24:19 +00:00
|
|
|
if (!shadow && waylandServer()) {
|
|
|
|
shadow = createShadowFromWayland(toplevel);
|
|
|
|
}
|
2017-09-20 17:35:21 +00:00
|
|
|
if (!shadow && kwinApp()->x11Connection()) {
|
2014-07-24 06:39:25 +00:00
|
|
|
shadow = createShadowFromX11(toplevel);
|
|
|
|
}
|
2020-01-01 01:11:17 +00:00
|
|
|
if (!shadow) {
|
|
|
|
shadow = createShadowFromInternalWindow(toplevel);
|
|
|
|
}
|
2018-09-13 10:59:06 +00:00
|
|
|
if (!shadow) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (toplevel->effectWindow() && toplevel->effectWindow()->sceneWindow()) {
|
|
|
|
toplevel->effectWindow()->sceneWindow()->updateShadow(shadow);
|
Try to invalidate quad cache when shadow is changed
Summary:
213239a0ea0a9c0967bb68d1eda7a8d4d6a09498 tried to address the case when
a wayland client gets shadow after it was mapped, but because of poor
testing from my side, another bug was introduced. If a decoration tooltip
or the user actions popup is shown, then in some cases it can be blank.
Usually, SurfaceInterface::shadowChanged proceeds SurfaceInterface::sizeChanged,
so when the shadow is installed, window quads cache is rebuilt. But
because shell client already knows the geometry of the internal client,
goemetryShapeChanged is not emitted, thus the cache is not updated.
It would be better just to invalidate the cache when the shadow is
installed, uninstalled, or updated. This reduces the number of
unnecessary invocations of Scene::Window::buildQuads and also moves
handling of the window quads cache away from the Shadow class.
BUG: 399490
FIXED-IN: 5.15.0
Test Plan: Decoration tooltips are no longer blank.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, graesslin, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D17215
2018-11-27 13:25:22 +00:00
|
|
|
emit toplevel->shadowChanged();
|
2014-07-24 06:39:25 +00:00
|
|
|
}
|
|
|
|
return shadow;
|
|
|
|
}
|
|
|
|
|
|
|
|
Shadow *Shadow::createShadowFromX11(Toplevel *toplevel)
|
|
|
|
{
|
2013-09-11 06:21:44 +00:00
|
|
|
auto data = Shadow::readX11ShadowProperty(toplevel->window());
|
2011-03-27 10:33:07 +00:00
|
|
|
if (!data.isEmpty()) {
|
2013-06-24 07:06:50 +00:00
|
|
|
Shadow *shadow = Compositor::self()->scene()->createShadow(toplevel);
|
2011-04-28 15:22:17 +00:00
|
|
|
|
2013-06-24 07:06:50 +00:00
|
|
|
if (!shadow->init(data)) {
|
|
|
|
delete shadow;
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
return nullptr;
|
2013-06-24 07:06:50 +00:00
|
|
|
}
|
2011-03-27 10:33:07 +00:00
|
|
|
return shadow;
|
|
|
|
} else {
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
return nullptr;
|
2011-03-27 10:33:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-03 17:35:22 +00:00
|
|
|
Shadow *Shadow::createShadowFromDecoration(Toplevel *toplevel)
|
2014-07-24 06:39:25 +00:00
|
|
|
{
|
2015-12-04 11:27:00 +00:00
|
|
|
AbstractClient *c = qobject_cast<AbstractClient*>(toplevel);
|
2014-07-24 06:39:25 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-07-15 09:24:19 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-01-01 01:11:17 +00:00
|
|
|
Shadow *Shadow::createShadowFromInternalWindow(Toplevel *toplevel)
|
|
|
|
{
|
|
|
|
const InternalClient *client = qobject_cast<InternalClient *>(toplevel);
|
|
|
|
if (!client) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
const QWindow *window = client->internalWindow();
|
|
|
|
if (!window) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
Shadow *shadow = Compositor::self()->scene()->createShadow(toplevel);
|
|
|
|
if (!shadow->init(window)) {
|
|
|
|
delete shadow;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return shadow;
|
|
|
|
}
|
|
|
|
|
2013-09-11 06:21:44 +00:00
|
|
|
QVector< uint32_t > Shadow::readX11ShadowProperty(xcb_window_t id)
|
2011-03-27 10:33:07 +00:00
|
|
|
{
|
2013-09-11 06:21:44 +00:00
|
|
|
QVector<uint32_t> ret;
|
2019-06-23 15:58:49 +00:00
|
|
|
if (id != XCB_WINDOW_NONE) {
|
2016-05-02 15:47:39 +00:00
|
|
|
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];
|
|
|
|
}
|
2011-03-27 10:33:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-09-11 06:21:44 +00:00
|
|
|
bool Shadow::init(const QVector< uint32_t > &data)
|
2011-03-27 10:33:07 +00:00
|
|
|
{
|
2013-07-31 13:25:39 +00:00
|
|
|
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);
|
2011-04-02 17:01:16 +00:00
|
|
|
return false;
|
|
|
|
}
|
2013-07-31 13:25:39 +00:00
|
|
|
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);
|
2011-03-27 10:33:07 +00:00
|
|
|
}
|
|
|
|
m_topOffset = data[ShadowElementsCount];
|
|
|
|
m_rightOffset = data[ShadowElementsCount+1];
|
|
|
|
m_bottomOffset = data[ShadowElementsCount+2];
|
|
|
|
m_leftOffset = data[ShadowElementsCount+3];
|
2011-04-03 10:43:57 +00:00
|
|
|
updateShadowRegion();
|
2011-06-23 17:06:12 +00:00
|
|
|
if (!prepareBackend()) {
|
|
|
|
return false;
|
|
|
|
}
|
2011-04-03 10:43:57 +00:00
|
|
|
buildQuads();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-07-24 06:39:25 +00:00
|
|
|
bool Shadow::init(KDecoration2::Decoration *decoration)
|
|
|
|
{
|
|
|
|
if (m_decorationShadow) {
|
|
|
|
// disconnect previous connections
|
2019-09-29 11:26:04 +00:00
|
|
|
disconnect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::innerShadowRectChanged, m_topLevel, &Toplevel::updateShadow);
|
|
|
|
disconnect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::shadowChanged, m_topLevel, &Toplevel::updateShadow);
|
|
|
|
disconnect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::paddingChanged, m_topLevel, &Toplevel::updateShadow);
|
2014-07-24 06:39:25 +00:00
|
|
|
}
|
2014-10-28 10:01:43 +00:00
|
|
|
m_decorationShadow = decoration->shadow();
|
2014-07-24 06:39:25 +00:00
|
|
|
if (!m_decorationShadow) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// setup connections - all just mapped to recreate
|
2019-09-29 11:26:04 +00:00
|
|
|
connect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::innerShadowRectChanged, m_topLevel, &Toplevel::updateShadow);
|
|
|
|
connect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::shadowChanged, m_topLevel, &Toplevel::updateShadow);
|
|
|
|
connect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::paddingChanged, m_topLevel, &Toplevel::updateShadow);
|
2014-11-11 14:18:35 +00:00
|
|
|
|
|
|
|
const QMargins &p = m_decorationShadow->padding();
|
|
|
|
m_topOffset = p.top();
|
|
|
|
m_rightOffset = p.right();
|
|
|
|
m_bottomOffset = p.bottom();
|
|
|
|
m_leftOffset = p.left();
|
2014-07-24 06:39:25 +00:00
|
|
|
updateShadowRegion();
|
|
|
|
if (!prepareBackend()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
buildQuads();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-07-15 09:24:19 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-01-01 01:11:17 +00:00
|
|
|
bool Shadow::init(const QWindow *window)
|
|
|
|
{
|
|
|
|
const bool isEnabled = window->property("kwin_shadow_enabled").toBool();
|
|
|
|
if (!isEnabled) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QImage leftTile = window->property("kwin_shadow_left_tile").value<QImage>();
|
|
|
|
const QImage topLeftTile = window->property("kwin_shadow_top_left_tile").value<QImage>();
|
|
|
|
const QImage topTile = window->property("kwin_shadow_top_tile").value<QImage>();
|
|
|
|
const QImage topRightTile = window->property("kwin_shadow_top_right_tile").value<QImage>();
|
|
|
|
const QImage rightTile = window->property("kwin_shadow_right_tile").value<QImage>();
|
|
|
|
const QImage bottomRightTile = window->property("kwin_shadow_bottom_right_tile").value<QImage>();
|
|
|
|
const QImage bottomTile = window->property("kwin_shadow_bottom_tile").value<QImage>();
|
|
|
|
const QImage bottomLeftTile = window->property("kwin_shadow_bottom_left_tile").value<QImage>();
|
|
|
|
|
|
|
|
m_shadowElements[ShadowElementLeft] = QPixmap::fromImage(leftTile);
|
|
|
|
m_shadowElements[ShadowElementTopLeft] = QPixmap::fromImage(topLeftTile);
|
|
|
|
m_shadowElements[ShadowElementTop] = QPixmap::fromImage(topTile);
|
|
|
|
m_shadowElements[ShadowElementTopRight] = QPixmap::fromImage(topRightTile);
|
|
|
|
m_shadowElements[ShadowElementRight] = QPixmap::fromImage(rightTile);
|
|
|
|
m_shadowElements[ShadowElementBottomRight] = QPixmap::fromImage(bottomRightTile);
|
|
|
|
m_shadowElements[ShadowElementBottom] = QPixmap::fromImage(bottomTile);
|
|
|
|
m_shadowElements[ShadowElementBottomLeft] = QPixmap::fromImage(bottomLeftTile);
|
|
|
|
|
|
|
|
const QMargins padding = window->property("kwin_shadow_padding").value<QMargins>();
|
|
|
|
|
|
|
|
m_leftOffset = padding.left();
|
|
|
|
m_topOffset = padding.top();
|
|
|
|
m_rightOffset = padding.right();
|
|
|
|
m_bottomOffset = padding.bottom();
|
|
|
|
|
|
|
|
updateShadowRegion();
|
|
|
|
|
|
|
|
if (!prepareBackend()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
buildQuads();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-04-03 10:43:57 +00:00
|
|
|
void Shadow::updateShadowRegion()
|
|
|
|
{
|
2011-11-25 14:21:12 +00:00
|
|
|
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);
|
2011-03-27 10:33:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Shadow::buildQuads()
|
|
|
|
{
|
|
|
|
// prepare window quads
|
|
|
|
m_shadowQuads.clear();
|
2011-11-25 14:21:12 +00:00
|
|
|
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())) {
|
2011-04-03 11:05:35 +00:00
|
|
|
// if our shadow is bigger than the window, we don't render the shadow
|
|
|
|
m_shadowRegion = QRegion();
|
|
|
|
return;
|
|
|
|
}
|
2011-11-25 14:21:12 +00:00
|
|
|
|
|
|
|
const QRect outerRect(QPoint(-m_leftOffset, -m_topOffset), QPoint(m_topLevel->width() + m_rightOffset, m_topLevel->height() + m_bottomOffset));
|
|
|
|
|
2011-03-27 10:33:07 +00:00
|
|
|
WindowQuad topLeftQuad(WindowQuadShadowTopLeft);
|
2011-11-25 14:21:12 +00:00
|
|
|
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);
|
2011-03-27 10:33:07 +00:00
|
|
|
m_shadowQuads.append(topLeftQuad);
|
2011-11-25 14:21:12 +00:00
|
|
|
|
2011-03-27 10:33:07 +00:00
|
|
|
WindowQuad topQuad(WindowQuadShadowTop);
|
2011-11-25 14:21:12 +00:00
|
|
|
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);
|
2011-03-27 10:33:07 +00:00
|
|
|
m_shadowQuads.append(topQuad);
|
2011-11-25 14:21:12 +00:00
|
|
|
|
2011-03-27 10:33:07 +00:00
|
|
|
WindowQuad topRightQuad(WindowQuadShadowTopRight);
|
2011-11-25 14:21:12 +00:00
|
|
|
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);
|
2011-03-27 10:33:07 +00:00
|
|
|
m_shadowQuads.append(topRightQuad);
|
2011-11-25 14:21:12 +00:00
|
|
|
|
2011-03-27 10:33:07 +00:00
|
|
|
WindowQuad rightQuad(WindowQuadShadowRight);
|
2011-11-25 14:21:12 +00:00
|
|
|
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);
|
2011-03-27 10:33:07 +00:00
|
|
|
m_shadowQuads.append(rightQuad);
|
2011-11-25 14:21:12 +00:00
|
|
|
|
2011-03-27 10:33:07 +00:00
|
|
|
WindowQuad bottomRightQuad(WindowQuadShadowBottomRight);
|
2011-11-25 14:21:12 +00:00
|
|
|
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);
|
2011-03-27 10:33:07 +00:00
|
|
|
m_shadowQuads.append(bottomRightQuad);
|
2011-11-25 14:21:12 +00:00
|
|
|
|
2011-03-27 10:33:07 +00:00
|
|
|
WindowQuad bottomQuad(WindowQuadShadowBottom);
|
2011-11-25 14:21:12 +00:00
|
|
|
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);
|
2011-03-27 10:33:07 +00:00
|
|
|
m_shadowQuads.append(bottomQuad);
|
2011-11-25 14:21:12 +00:00
|
|
|
|
2011-03-27 10:33:07 +00:00
|
|
|
WindowQuad bottomLeftQuad(WindowQuadShadowBottomLeft);
|
2011-11-25 14:21:12 +00:00
|
|
|
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);
|
2011-03-27 10:33:07 +00:00
|
|
|
m_shadowQuads.append(bottomLeftQuad);
|
2011-11-25 14:21:12 +00:00
|
|
|
|
2011-03-27 10:33:07 +00:00
|
|
|
WindowQuad leftQuad(WindowQuadShadowLeft);
|
2011-11-25 14:21:12 +00:00
|
|
|
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);
|
2011-03-27 10:33:07 +00:00
|
|
|
m_shadowQuads.append(leftQuad);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Shadow::updateShadow()
|
|
|
|
{
|
Try to invalidate quad cache when shadow is changed
Summary:
213239a0ea0a9c0967bb68d1eda7a8d4d6a09498 tried to address the case when
a wayland client gets shadow after it was mapped, but because of poor
testing from my side, another bug was introduced. If a decoration tooltip
or the user actions popup is shown, then in some cases it can be blank.
Usually, SurfaceInterface::shadowChanged proceeds SurfaceInterface::sizeChanged,
so when the shadow is installed, window quads cache is rebuilt. But
because shell client already knows the geometry of the internal client,
goemetryShapeChanged is not emitted, thus the cache is not updated.
It would be better just to invalidate the cache when the shadow is
installed, uninstalled, or updated. This reduces the number of
unnecessary invocations of Scene::Window::buildQuads and also moves
handling of the window quads cache away from the Shadow class.
BUG: 399490
FIXED-IN: 5.15.0
Test Plan: Decoration tooltips are no longer blank.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, graesslin, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D17215
2018-11-27 13:25:22 +00:00
|
|
|
if (!m_topLevel) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-07-24 06:39:25 +00:00
|
|
|
if (m_decorationShadow) {
|
2015-12-04 11:27:00 +00:00
|
|
|
if (AbstractClient *c = qobject_cast<AbstractClient*>(m_topLevel)) {
|
2014-07-24 06:39:25 +00:00
|
|
|
if (c->decoration()) {
|
|
|
|
if (init(c->decoration())) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
Try to invalidate quad cache when shadow is changed
Summary:
213239a0ea0a9c0967bb68d1eda7a8d4d6a09498 tried to address the case when
a wayland client gets shadow after it was mapped, but because of poor
testing from my side, another bug was introduced. If a decoration tooltip
or the user actions popup is shown, then in some cases it can be blank.
Usually, SurfaceInterface::shadowChanged proceeds SurfaceInterface::sizeChanged,
so when the shadow is installed, window quads cache is rebuilt. But
because shell client already knows the geometry of the internal client,
goemetryShapeChanged is not emitted, thus the cache is not updated.
It would be better just to invalidate the cache when the shadow is
installed, uninstalled, or updated. This reduces the number of
unnecessary invocations of Scene::Window::buildQuads and also moves
handling of the window quads cache away from the Shadow class.
BUG: 399490
FIXED-IN: 5.15.0
Test Plan: Decoration tooltips are no longer blank.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, graesslin, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D17215
2018-11-27 13:25:22 +00:00
|
|
|
|
2015-07-15 09:24:19 +00:00
|
|
|
if (waylandServer()) {
|
|
|
|
if (m_topLevel && m_topLevel->surface()) {
|
|
|
|
if (const auto &s = m_topLevel->surface()->shadow()) {
|
|
|
|
if (init(s)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
Try to invalidate quad cache when shadow is changed
Summary:
213239a0ea0a9c0967bb68d1eda7a8d4d6a09498 tried to address the case when
a wayland client gets shadow after it was mapped, but because of poor
testing from my side, another bug was introduced. If a decoration tooltip
or the user actions popup is shown, then in some cases it can be blank.
Usually, SurfaceInterface::shadowChanged proceeds SurfaceInterface::sizeChanged,
so when the shadow is installed, window quads cache is rebuilt. But
because shell client already knows the geometry of the internal client,
goemetryShapeChanged is not emitted, thus the cache is not updated.
It would be better just to invalidate the cache when the shadow is
installed, uninstalled, or updated. This reduces the number of
unnecessary invocations of Scene::Window::buildQuads and also moves
handling of the window quads cache away from the Shadow class.
BUG: 399490
FIXED-IN: 5.15.0
Test Plan: Decoration tooltips are no longer blank.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, graesslin, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D17215
2018-11-27 13:25:22 +00:00
|
|
|
|
2020-01-01 01:11:17 +00:00
|
|
|
if (InternalClient *client = qobject_cast<InternalClient *>(m_topLevel)) {
|
|
|
|
if (init(client->internalWindow())) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-24 06:39:25 +00:00
|
|
|
auto data = Shadow::readX11ShadowProperty(m_topLevel->window());
|
|
|
|
if (data.isEmpty()) {
|
2011-03-27 10:33:07 +00:00
|
|
|
return false;
|
|
|
|
}
|
Try to invalidate quad cache when shadow is changed
Summary:
213239a0ea0a9c0967bb68d1eda7a8d4d6a09498 tried to address the case when
a wayland client gets shadow after it was mapped, but because of poor
testing from my side, another bug was introduced. If a decoration tooltip
or the user actions popup is shown, then in some cases it can be blank.
Usually, SurfaceInterface::shadowChanged proceeds SurfaceInterface::sizeChanged,
so when the shadow is installed, window quads cache is rebuilt. But
because shell client already knows the geometry of the internal client,
goemetryShapeChanged is not emitted, thus the cache is not updated.
It would be better just to invalidate the cache when the shadow is
installed, uninstalled, or updated. This reduces the number of
unnecessary invocations of Scene::Window::buildQuads and also moves
handling of the window quads cache away from the Shadow class.
BUG: 399490
FIXED-IN: 5.15.0
Test Plan: Decoration tooltips are no longer blank.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, graesslin, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D17215
2018-11-27 13:25:22 +00:00
|
|
|
|
2011-03-27 10:33:07 +00:00
|
|
|
init(data);
|
Try to invalidate quad cache when shadow is changed
Summary:
213239a0ea0a9c0967bb68d1eda7a8d4d6a09498 tried to address the case when
a wayland client gets shadow after it was mapped, but because of poor
testing from my side, another bug was introduced. If a decoration tooltip
or the user actions popup is shown, then in some cases it can be blank.
Usually, SurfaceInterface::shadowChanged proceeds SurfaceInterface::sizeChanged,
so when the shadow is installed, window quads cache is rebuilt. But
because shell client already knows the geometry of the internal client,
goemetryShapeChanged is not emitted, thus the cache is not updated.
It would be better just to invalidate the cache when the shadow is
installed, uninstalled, or updated. This reduces the number of
unnecessary invocations of Scene::Window::buildQuads and also moves
handling of the window quads cache away from the Shadow class.
BUG: 399490
FIXED-IN: 5.15.0
Test Plan: Decoration tooltips are no longer blank.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, graesslin, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D17215
2018-11-27 13:25:22 +00:00
|
|
|
|
2011-03-27 10:33:07 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-04-01 19:49:44 +00:00
|
|
|
void Shadow::setToplevel(Toplevel *topLevel)
|
|
|
|
{
|
|
|
|
m_topLevel = topLevel;
|
2020-02-05 09:28:50 +00:00
|
|
|
connect(m_topLevel, &Toplevel::frameGeometryChanged, this, &Shadow::geometryChanged);
|
2011-04-03 10:43:57 +00:00
|
|
|
}
|
|
|
|
void Shadow::geometryChanged()
|
|
|
|
{
|
2019-09-27 10:01:10 +00:00
|
|
|
if (m_cachedSize == m_topLevel->size()) {
|
2011-04-03 10:43:57 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-09-27 10:01:10 +00:00
|
|
|
m_cachedSize = m_topLevel->size();
|
2011-04-03 10:43:57 +00:00
|
|
|
updateShadowRegion();
|
|
|
|
buildQuads();
|
2011-04-01 19:49:44 +00:00
|
|
|
}
|
|
|
|
|
2014-07-24 06:39:25 +00:00
|
|
|
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:
|
2014-11-12 08:06:03 +00:00
|
|
|
return m_decorationShadow->topGeometry().size();
|
2014-07-24 06:39:25 +00:00
|
|
|
case ShadowElementTopRight:
|
2014-11-12 08:06:03 +00:00
|
|
|
return m_decorationShadow->topRightGeometry().size();
|
2014-07-24 06:39:25 +00:00
|
|
|
case ShadowElementRight:
|
2014-11-12 08:06:03 +00:00
|
|
|
return m_decorationShadow->rightGeometry().size();
|
2014-07-24 06:39:25 +00:00
|
|
|
case ShadowElementBottomRight:
|
2014-11-12 08:06:03 +00:00
|
|
|
return m_decorationShadow->bottomRightGeometry().size();
|
2014-07-24 06:39:25 +00:00
|
|
|
case ShadowElementBottom:
|
2014-11-12 08:06:03 +00:00
|
|
|
return m_decorationShadow->bottomGeometry().size();
|
2014-07-24 06:39:25 +00:00
|
|
|
case ShadowElementBottomLeft:
|
2014-11-12 08:06:03 +00:00
|
|
|
return m_decorationShadow->bottomLeftGeometry().size();
|
2014-07-24 06:39:25 +00:00
|
|
|
case ShadowElementLeft:
|
2014-11-12 08:06:03 +00:00
|
|
|
return m_decorationShadow->leftGeometry().size();
|
2014-07-24 06:39:25 +00:00
|
|
|
case ShadowElementTopLeft:
|
2014-11-12 08:06:03 +00:00
|
|
|
return m_decorationShadow->topLeftGeometry().size();
|
2014-07-24 06:39:25 +00:00
|
|
|
default:
|
|
|
|
return QSize();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return m_shadowElements[element].size();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-12 09:13:47 +00:00
|
|
|
void Shadow::setShadowElement(const QPixmap &shadow, Shadow::ShadowElements element)
|
|
|
|
{
|
|
|
|
m_shadowElements[element] = shadow;
|
|
|
|
}
|
|
|
|
|
2011-03-27 10:33:07 +00:00
|
|
|
} // namespace
|