2007-11-27 19:40:25 +00:00
|
|
|
/********************************************************************
|
2007-04-29 17:35:43 +00:00
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
|
|
|
|
|
|
|
Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
|
|
|
|
|
2007-11-27 19:40:25 +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/>.
|
|
|
|
*********************************************************************/
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
(NOTE: The compositing code is work in progress. As such this design
|
|
|
|
documentation may get outdated in some areas.)
|
|
|
|
|
|
|
|
The base class for compositing, implementing shared functionality
|
|
|
|
between the OpenGL and XRender backends.
|
2011-01-30 14:34:42 +00:00
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
Design:
|
2011-01-30 14:34:42 +00:00
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
When compositing is turned on, XComposite extension is used to redirect
|
|
|
|
drawing of windows to pixmaps and XDamage extension is used to get informed
|
|
|
|
about damage (changes) to window contents. This code is mostly in composite.cpp .
|
2011-01-30 14:34:42 +00:00
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
Workspace::performCompositing() starts one painting pass. Painting is done
|
|
|
|
by painting the screen, which in turn paints every window. Painting can be affected
|
|
|
|
using effects, which are chained. E.g. painting a screen means that actually
|
|
|
|
paintScreen() of the first effect is called, which possibly does modifications
|
|
|
|
and calls next effect's paintScreen() and so on, until Scene::finalPaintScreen()
|
|
|
|
is called.
|
2011-01-30 14:34:42 +00:00
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
There are 3 phases of every paint (not necessarily done together):
|
|
|
|
The pre-paint phase, the paint phase and the post-paint phase.
|
2011-01-30 14:34:42 +00:00
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
The pre-paint phase is used to find out about how the painting will be actually
|
|
|
|
done (i.e. what the effects will do). For example when only a part of the screen
|
|
|
|
needs to be updated and no effect will do any transformation it is possible to use
|
|
|
|
an optimized paint function. How the painting will be done is controlled
|
|
|
|
by the mask argument, see PAINT_WINDOW_* and PAINT_SCREEN_* flags in scene.h .
|
|
|
|
For example an effect that decides to paint a normal windows as translucent
|
|
|
|
will need to modify the mask in its prePaintWindow() to include
|
|
|
|
the PAINT_WINDOW_TRANSLUCENT flag. The paintWindow() function will then get
|
|
|
|
the mask with this flag turned on and will also paint using transparency.
|
2011-01-30 14:34:42 +00:00
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
The paint pass does the actual painting, based on the information collected
|
|
|
|
using the pre-paint pass. After running through the effects' paintScreen()
|
|
|
|
either paintGenericScreen() or optimized paintSimpleScreen() are called.
|
|
|
|
Those call paintWindow() on windows (not necessarily all), possibly using
|
|
|
|
clipping to optimize performance and calling paintWindow() first with only
|
|
|
|
PAINT_WINDOW_OPAQUE to paint the opaque parts and then later
|
|
|
|
with PAINT_WINDOW_TRANSLUCENT to paint the transparent parts. Function
|
|
|
|
paintWindow() again goes through effects' paintWindow() until
|
|
|
|
finalPaintWindow() is called, which calls the window's performPaint() to
|
|
|
|
do the actual painting.
|
2011-01-30 14:34:42 +00:00
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
The post-paint can be used for cleanups and is also used for scheduling
|
|
|
|
repaints during the next painting pass for animations. Effects wanting to
|
|
|
|
repaint certain parts can manually damage them during post-paint and repaint
|
|
|
|
of these parts will be done during the next paint pass.
|
2011-01-30 14:34:42 +00:00
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "scene.h"
|
|
|
|
|
|
|
|
#include <X11/extensions/shape.h>
|
|
|
|
|
|
|
|
#include "client.h"
|
|
|
|
#include "deleted.h"
|
|
|
|
#include "effects.h"
|
2010-06-02 20:04:54 +00:00
|
|
|
#include "lanczosfilter.h"
|
2011-07-06 09:58:23 +00:00
|
|
|
#include "overlaywindow.h"
|
2011-03-27 10:33:07 +00:00
|
|
|
#include "shadow.h"
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2008-11-15 11:38:26 +00:00
|
|
|
#include <kephal/screens.h>
|
2011-11-10 13:28:06 +00:00
|
|
|
#include "thumbnailitem.h"
|
2008-09-18 15:27:13 +00:00
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
|
|
|
//****************************************
|
|
|
|
// Scene
|
|
|
|
//****************************************
|
|
|
|
|
2011-02-27 22:39:32 +00:00
|
|
|
Scene* scene = 0;
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
Scene::Scene(Workspace* ws)
|
2011-06-15 16:35:00 +00:00
|
|
|
: QObject(ws)
|
2011-10-29 15:03:55 +00:00
|
|
|
, lastRenderTime(0)
|
2011-06-15 16:35:00 +00:00
|
|
|
, wspace(ws)
|
2011-01-30 14:34:42 +00:00
|
|
|
, has_waitSync(false)
|
|
|
|
, lanczos_filter(new LanczosFilter())
|
2011-07-06 09:58:23 +00:00
|
|
|
, m_overlayWindow(new OverlayWindow())
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2011-10-29 14:55:56 +00:00
|
|
|
last_time.invalidate(); // Initialize the timer
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
Scene::~Scene()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2010-06-02 20:04:54 +00:00
|
|
|
delete lanczos_filter;
|
2011-07-06 09:58:23 +00:00
|
|
|
delete m_overlayWindow;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// returns mask and possibly modified region
|
2011-01-30 14:34:42 +00:00
|
|
|
void Scene::paintScreen(int* mask, QRegion* region)
|
|
|
|
{
|
|
|
|
*mask = (*region == QRegion(0, 0, displayWidth(), displayHeight()))
|
|
|
|
? 0 : PAINT_SCREEN_REGION;
|
2007-04-29 17:35:43 +00:00
|
|
|
updateTimeDiff();
|
|
|
|
// preparation step
|
|
|
|
static_cast<EffectsHandlerImpl*>(effects)->startPaint();
|
2011-07-06 08:01:23 +00:00
|
|
|
|
2007-07-07 14:01:32 +00:00
|
|
|
ScreenPrePaintData pdata;
|
|
|
|
pdata.mask = *mask;
|
|
|
|
pdata.paint = *region;
|
2011-07-06 08:01:23 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
effects->prePaintScreen(pdata, time_diff);
|
2007-07-07 14:01:32 +00:00
|
|
|
*mask = pdata.mask;
|
2012-01-29 11:03:12 +00:00
|
|
|
*region = pdata.paint;
|
2011-07-06 08:01:23 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if (*mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) {
|
|
|
|
// Region painting is not possible with transformations,
|
|
|
|
// because screen damage doesn't match transformed positions.
|
2007-04-29 17:35:43 +00:00
|
|
|
*mask &= ~PAINT_SCREEN_REGION;
|
|
|
|
*region = infiniteRegion();
|
2011-01-30 14:34:42 +00:00
|
|
|
} else if (*mask & PAINT_SCREEN_REGION) {
|
|
|
|
// make sure not to go outside visible screen
|
|
|
|
*region &= QRegion(0, 0, displayWidth(), displayHeight());
|
|
|
|
} else {
|
|
|
|
// whole screen, not transformed, force region to be full
|
|
|
|
*region = QRegion(0, 0, displayWidth(), displayHeight());
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
painted_region = *region;
|
2012-01-29 17:20:50 +00:00
|
|
|
if (*mask & PAINT_SCREEN_BACKGROUND_FIRST) {
|
2011-01-30 14:34:42 +00:00
|
|
|
paintBackground(*region);
|
2012-01-29 17:20:50 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
ScreenPaintData data;
|
2011-01-30 14:34:42 +00:00
|
|
|
effects->paintScreen(*mask, *region, data);
|
2012-01-29 17:20:50 +00:00
|
|
|
foreach (Window * w, stacking_order) {
|
|
|
|
effects->postPaintWindow(effectWindow(w));
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
effects->postPaintScreen();
|
|
|
|
*region |= painted_region;
|
|
|
|
// make sure not to go outside of the screen area
|
2011-01-30 14:34:42 +00:00
|
|
|
*region &= QRegion(0, 0, displayWidth(), displayHeight());
|
2008-02-25 11:32:21 +00:00
|
|
|
// make sure all clipping is restored
|
2011-01-30 14:34:42 +00:00
|
|
|
Q_ASSERT(!PaintClipper::clip());
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// Compute time since the last painting pass.
|
|
|
|
void Scene::updateTimeDiff()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2011-08-29 04:47:07 +00:00
|
|
|
if (!last_time.isValid()) {
|
2007-04-29 17:35:43 +00:00
|
|
|
// Painting has been idle (optimized out) for some time,
|
|
|
|
// which means time_diff would be huge and would break animations.
|
|
|
|
// Simply set it to one (zero would mean no change at all and could
|
|
|
|
// cause problems).
|
|
|
|
time_diff = 1;
|
2011-10-29 15:36:26 +00:00
|
|
|
last_time.start();
|
2011-01-30 14:34:42 +00:00
|
|
|
} else
|
2011-10-29 15:36:26 +00:00
|
|
|
time_diff = last_time.restart();
|
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if (time_diff < 0) // check time rollback
|
2007-04-29 17:35:43 +00:00
|
|
|
time_diff = 1;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// Painting pass is optimized away.
|
|
|
|
void Scene::idle()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2007-04-29 17:35:43 +00:00
|
|
|
// Don't break time since last paint for the next pass.
|
2011-10-29 15:06:54 +00:00
|
|
|
last_time.invalidate();
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// the function that'll be eventually called by paintScreen() above
|
2011-01-30 14:34:42 +00:00
|
|
|
void Scene::finalPaintScreen(int mask, QRegion region, ScreenPaintData& data)
|
|
|
|
{
|
2012-01-29 16:25:20 +00:00
|
|
|
if (mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS))
|
2011-01-30 14:34:42 +00:00
|
|
|
paintGenericScreen(mask, data);
|
2007-04-29 17:35:43 +00:00
|
|
|
else
|
2011-01-30 14:34:42 +00:00
|
|
|
paintSimpleScreen(mask, region);
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// The generic painting code that can handle even transformations.
|
|
|
|
// It simply paints bottom-to-top.
|
2011-01-30 14:34:42 +00:00
|
|
|
void Scene::paintGenericScreen(int orig_mask, ScreenPaintData)
|
|
|
|
{
|
2012-01-29 17:20:50 +00:00
|
|
|
if (!(orig_mask & PAINT_SCREEN_BACKGROUND_FIRST)) {
|
2011-01-30 14:34:42 +00:00
|
|
|
paintBackground(infiniteRegion());
|
2012-01-29 17:20:50 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
QList< Phase2Data > phase2;
|
2011-01-30 14:34:42 +00:00
|
|
|
foreach (Window * w, stacking_order) { // bottom to top
|
2011-07-06 08:01:23 +00:00
|
|
|
Toplevel* topw = w->window();
|
|
|
|
|
|
|
|
// Reset the repaint_region.
|
|
|
|
// This has to be done here because many effects schedule a repaint for
|
|
|
|
// the next frame within Effects::prePaintWindow.
|
2012-02-07 16:01:41 +00:00
|
|
|
topw->resetRepaints();
|
2011-07-06 08:01:23 +00:00
|
|
|
|
2007-07-07 14:01:32 +00:00
|
|
|
WindowPrePaintData data;
|
2011-01-30 14:34:42 +00:00
|
|
|
data.mask = orig_mask | (w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT);
|
2007-04-29 17:35:43 +00:00
|
|
|
w->resetPaintingEnabled();
|
2007-07-07 14:01:32 +00:00
|
|
|
data.paint = infiniteRegion(); // no clipping, so doesn't really matter
|
|
|
|
data.clip = QRegion();
|
2007-07-18 15:01:59 +00:00
|
|
|
data.quads = w->buildQuads();
|
2007-04-29 17:35:43 +00:00
|
|
|
// preparation step
|
2011-01-30 14:34:42 +00:00
|
|
|
effects->prePaintWindow(effectWindow(w), data, time_diff);
|
2007-07-19 16:11:27 +00:00
|
|
|
#ifndef NDEBUG
|
2012-01-29 16:25:20 +00:00
|
|
|
if (data.quads.isTransformed()) {
|
2011-01-30 14:34:42 +00:00
|
|
|
kFatal(1212) << "Pre-paint calls are not allowed to transform quads!" ;
|
2012-01-29 16:25:20 +00:00
|
|
|
}
|
2007-07-19 16:11:27 +00:00
|
|
|
#endif
|
2012-01-29 17:20:50 +00:00
|
|
|
if (!w->isPaintingEnabled()) {
|
2007-04-29 17:35:43 +00:00
|
|
|
continue;
|
2012-01-29 17:20:50 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
phase2.append(Phase2Data(w, infiniteRegion(), data.clip, data.mask, data.quads));
|
2008-08-24 13:32:57 +00:00
|
|
|
// transformations require window pixmap
|
2011-01-30 14:34:42 +00:00
|
|
|
w->suspendUnredirect(data.mask
|
|
|
|
& (PAINT_WINDOW_TRANSLUCENT | PAINT_SCREEN_TRANSFORMED | PAINT_WINDOW_TRANSFORMED));
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
|
|
|
|
2012-01-29 16:25:20 +00:00
|
|
|
foreach (const Phase2Data & d, phase2) {
|
|
|
|
paintWindow(d.window, d.mask, d.region, d.quads);
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
// The optimized case without any transformations at all.
|
|
|
|
// It can paint only the requested region and can use clipping
|
|
|
|
// to reduce painting and improve performance.
|
2011-01-30 14:34:42 +00:00
|
|
|
void Scene::paintSimpleScreen(int orig_mask, QRegion region)
|
|
|
|
{
|
2012-01-29 16:25:20 +00:00
|
|
|
assert((orig_mask & (PAINT_SCREEN_TRANSFORMED
|
|
|
|
| PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) == 0);
|
2012-01-10 17:56:14 +00:00
|
|
|
QList< QPair< Window*, Phase2Data > > phase2data;
|
2011-07-06 08:01:23 +00:00
|
|
|
|
|
|
|
QRegion dirtyArea = region;
|
|
|
|
for (int i = 0; // do prePaintWindow bottom to top
|
|
|
|
i < stacking_order.count();
|
|
|
|
++i) {
|
2007-04-29 17:35:43 +00:00
|
|
|
Window* w = stacking_order[ i ];
|
2011-07-06 08:01:23 +00:00
|
|
|
Toplevel* topw = w->window();
|
2007-07-07 14:01:32 +00:00
|
|
|
WindowPrePaintData data;
|
2011-01-30 14:34:42 +00:00
|
|
|
data.mask = orig_mask | (w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT);
|
2007-04-29 17:35:43 +00:00
|
|
|
w->resetPaintingEnabled();
|
2007-07-07 14:01:32 +00:00
|
|
|
data.paint = region;
|
2012-02-07 16:01:41 +00:00
|
|
|
data.paint |= topw->repaints();
|
2011-07-06 08:01:23 +00:00
|
|
|
data.paint |= topw->decorationPendingRegion();
|
|
|
|
|
|
|
|
// Reset the repaint_region.
|
|
|
|
// This has to be done here because many effects schedule a repaint for
|
|
|
|
// the next frame within Effects::prePaintWindow.
|
2012-02-07 16:01:41 +00:00
|
|
|
topw->resetRepaints();
|
2012-02-21 18:22:08 +00:00
|
|
|
|
2009-04-22 17:29:56 +00:00
|
|
|
// Clip out the decoration for opaque windows; the decoration is drawn in the second pass
|
2011-10-22 09:02:49 +00:00
|
|
|
if (w->isOpaque()) {
|
|
|
|
// the window is fully opaque
|
|
|
|
data.clip = w->clientShape().translated(w->x(), w->y());
|
|
|
|
} else if (topw->hasAlpha() && topw->opacity() == 1.0) {
|
|
|
|
// the window is partially opaque
|
2012-02-08 15:41:02 +00:00
|
|
|
data.clip = (w->clientShape() & topw->opaqueRegion().translated(topw->clientPos())).translated(w->x(), w->y());
|
2011-10-22 09:02:49 +00:00
|
|
|
} else {
|
|
|
|
data.clip = QRegion();
|
|
|
|
}
|
2007-07-18 15:01:59 +00:00
|
|
|
data.quads = w->buildQuads();
|
2007-04-29 17:35:43 +00:00
|
|
|
// preparation step
|
2011-01-30 14:34:42 +00:00
|
|
|
effects->prePaintWindow(effectWindow(w), data, time_diff);
|
2007-07-19 16:11:27 +00:00
|
|
|
#ifndef NDEBUG
|
2012-01-29 16:25:20 +00:00
|
|
|
if (data.quads.isTransformed()) {
|
2011-01-30 14:34:42 +00:00
|
|
|
kFatal(1212) << "Pre-paint calls are not allowed to transform quads!" ;
|
2012-01-29 16:25:20 +00:00
|
|
|
}
|
2007-07-19 16:11:27 +00:00
|
|
|
#endif
|
2011-01-30 14:34:42 +00:00
|
|
|
if (!w->isPaintingEnabled()) {
|
|
|
|
w->suspendUnredirect(true);
|
2007-04-29 17:35:43 +00:00
|
|
|
continue;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2011-07-14 12:17:33 +00:00
|
|
|
dirtyArea |= data.paint;
|
2007-12-07 17:03:59 +00:00
|
|
|
// Schedule the window for painting
|
2012-01-10 17:56:14 +00:00
|
|
|
phase2data.append(QPair< Window*, Phase2Data >(w,Phase2Data(w, data.paint, data.clip,
|
|
|
|
data.mask, data.quads)));
|
2008-08-24 13:32:57 +00:00
|
|
|
// no transformations, but translucency requires window pixmap
|
2011-01-30 14:34:42 +00:00
|
|
|
w->suspendUnredirect(data.mask & PAINT_WINDOW_TRANSLUCENT);
|
|
|
|
}
|
2012-01-10 17:56:14 +00:00
|
|
|
|
|
|
|
// This is the occlusion culling pass
|
2011-07-06 08:01:23 +00:00
|
|
|
QRegion allclips, upperTranslucentDamage;
|
2012-01-10 17:56:14 +00:00
|
|
|
for (int i = phase2data.count() - 1; i >= 0; --i) {
|
|
|
|
QPair< Window*, Phase2Data > *entry = &phase2data[i];
|
|
|
|
Phase2Data *data = &entry->second;
|
2011-07-06 08:01:23 +00:00
|
|
|
|
2012-02-07 16:01:41 +00:00
|
|
|
data->region |= upperTranslucentDamage;
|
|
|
|
|
2012-01-10 17:56:14 +00:00
|
|
|
// subtract the parts which will possibly been drawn as part of
|
2011-07-06 08:01:23 +00:00
|
|
|
// a higher opaque window
|
|
|
|
data->region -= allclips;
|
|
|
|
|
2011-10-22 09:02:49 +00:00
|
|
|
// Here we rely on WindowPrePaintData::setTranslucent() to remove
|
|
|
|
// the clip if needed.
|
2012-01-29 16:25:20 +00:00
|
|
|
if (!data->clip.isEmpty() && !(data->mask & PAINT_WINDOW_TRANSFORMED)) {
|
2012-01-10 17:56:14 +00:00
|
|
|
// clip away the opaque regions for all windows below this one
|
2011-07-06 08:01:23 +00:00
|
|
|
allclips |= data->clip;
|
2012-01-10 17:56:14 +00:00
|
|
|
// extend the translucent damage for windows below this by remaining (translucent) regions
|
|
|
|
upperTranslucentDamage |= data->region - data->clip;
|
|
|
|
} else {
|
|
|
|
upperTranslucentDamage |= data->region;
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2011-07-06 08:01:23 +00:00
|
|
|
|
2012-01-10 17:56:14 +00:00
|
|
|
QRegion paintedArea;
|
2011-07-06 08:01:23 +00:00
|
|
|
// Fill any areas of the root window not covered by opaque windows
|
|
|
|
if (!(orig_mask & PAINT_SCREEN_BACKGROUND_FIRST)) {
|
2012-01-10 17:56:14 +00:00
|
|
|
paintedArea = dirtyArea - allclips;
|
|
|
|
paintBackground(paintedArea);
|
2011-07-06 08:01:23 +00:00
|
|
|
}
|
|
|
|
|
2012-01-10 17:56:14 +00:00
|
|
|
// Now walk the list bottom to top and draw the windows.
|
|
|
|
for (int i = 0; i < phase2data.count(); ++i) {
|
|
|
|
Phase2Data *data = &phase2data[i].second;
|
2011-07-06 08:01:23 +00:00
|
|
|
|
2012-01-10 17:56:14 +00:00
|
|
|
// add all regions which have been drawn so far
|
|
|
|
paintedArea |= data->region;
|
|
|
|
data->region = paintedArea;
|
2011-07-06 08:01:23 +00:00
|
|
|
|
|
|
|
paintWindow(data->window, data->mask, data->region, data->quads);
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
2012-01-10 17:56:14 +00:00
|
|
|
painted_region |= paintedArea;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
void Scene::paintWindow(Window* w, int mask, QRegion region, WindowQuadList quads)
|
|
|
|
{
|
2007-12-07 17:03:59 +00:00
|
|
|
// no painting outside visible screen (and no transformations)
|
2011-01-30 14:34:42 +00:00
|
|
|
region &= QRect(0, 0, displayWidth(), displayHeight());
|
|
|
|
if (region.isEmpty()) // completely clipped
|
2007-12-07 17:03:59 +00:00
|
|
|
return;
|
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
WindowPaintData data(w->window()->effectWindow());
|
2007-07-07 14:01:32 +00:00
|
|
|
data.quads = quads;
|
2011-01-30 14:34:42 +00:00
|
|
|
effects->paintWindow(effectWindow(w), mask, region, data);
|
2011-11-10 13:28:06 +00:00
|
|
|
// paint thumbnails on top of window
|
|
|
|
EffectWindowImpl *wImpl = static_cast<EffectWindowImpl*>(effectWindow(w));
|
|
|
|
for (QHash<ThumbnailItem*, QWeakPointer<EffectWindowImpl> >::const_iterator it = wImpl->thumbnails().constBegin();
|
|
|
|
it != wImpl->thumbnails().constEnd();
|
|
|
|
++it) {
|
|
|
|
if (it.value().isNull()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ThumbnailItem *item = it.key();
|
|
|
|
if (!item->isVisible()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
EffectWindowImpl *thumb = it.value().data();
|
|
|
|
WindowPaintData thumbData(thumb);
|
|
|
|
thumbData.opacity = data.opacity;
|
|
|
|
|
|
|
|
QSizeF size = QSizeF(thumb->size());
|
|
|
|
size.scale(QSizeF(item->sceneBoundingRect().width(), item->sceneBoundingRect().height()), Qt::KeepAspectRatio);
|
|
|
|
thumbData.xScale = size.width() / static_cast<qreal>(thumb->width());
|
|
|
|
thumbData.yScale = size.height() / static_cast<qreal>(thumb->height());
|
|
|
|
const int x = item->scenePos().x() + w->x() + (item->width() - size.width()) / 2;
|
|
|
|
const int y = item->scenePos().y() + w->y() + (item->height() - size.height()) / 2;
|
|
|
|
thumbData.xTranslate = x - thumb->x();
|
|
|
|
thumbData.yTranslate = y - thumb->y();
|
|
|
|
int thumbMask = PAINT_WINDOW_TRANSFORMED | PAINT_WINDOW_LANCZOS;
|
|
|
|
if (thumbData.opacity == 1.0) {
|
|
|
|
thumbMask |= PAINT_WINDOW_OPAQUE;
|
|
|
|
} else {
|
|
|
|
thumbMask |= PAINT_WINDOW_TRANSLUCENT;
|
|
|
|
}
|
2011-11-29 06:13:26 +00:00
|
|
|
if (item->isClip() && (x < wImpl->x() || x + size.width() > wImpl->x() + wImpl->width() ||
|
|
|
|
y < wImpl->y() || y + size.height() > wImpl->y() + wImpl->height())) {
|
2011-11-10 13:28:06 +00:00
|
|
|
// don't render windows outside the containing window.
|
|
|
|
// TODO: improve by spliting out the window quads which do not fit
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
effects->drawWindow(thumb, thumbMask, QRegion(x, y, size.width(), size.height()), thumbData);
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// the function that'll be eventually called by paintWindow() above
|
2011-01-30 14:34:42 +00:00
|
|
|
void Scene::finalPaintWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data)
|
|
|
|
{
|
|
|
|
effects->drawWindow(w, mask, region, data);
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// will be eventually called from drawWindow()
|
2011-01-30 14:34:42 +00:00
|
|
|
void Scene::finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data)
|
|
|
|
{
|
|
|
|
if (mask & PAINT_WINDOW_LANCZOS)
|
|
|
|
lanczos_filter->performPaint(w, mask, region, data);
|
2010-06-02 20:04:54 +00:00
|
|
|
else
|
2011-01-30 14:34:42 +00:00
|
|
|
w->sceneWindow()->performPaint(mask, region, data);
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-07-06 09:58:23 +00:00
|
|
|
OverlayWindow* Scene::overlayWindow()
|
|
|
|
{
|
|
|
|
return m_overlayWindow;
|
|
|
|
}
|
|
|
|
|
2011-11-26 15:15:46 +00:00
|
|
|
void Scene::screenGeometryChanged(const QSize &size)
|
|
|
|
{
|
|
|
|
m_overlayWindow->resize(size);
|
|
|
|
}
|
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
//****************************************
|
|
|
|
// Scene::Window
|
|
|
|
//****************************************
|
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
Scene::Window::Window(Toplevel * c)
|
|
|
|
: toplevel(c)
|
|
|
|
, filter(ImageFilterFast)
|
2011-04-03 09:31:33 +00:00
|
|
|
, m_shadow(NULL)
|
2011-01-30 14:34:42 +00:00
|
|
|
, disable_painting(0)
|
|
|
|
, shape_valid(false)
|
|
|
|
, cached_quad_list(NULL)
|
|
|
|
{
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
Scene::Window::~Window()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2008-03-20 11:17:50 +00:00
|
|
|
delete cached_quad_list;
|
2011-04-03 09:31:33 +00:00
|
|
|
delete m_shadow;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
void Scene::Window::discardShape()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2007-04-29 17:35:43 +00:00
|
|
|
// it is created on-demand and cached, simply
|
|
|
|
// reset the flag
|
|
|
|
shape_valid = false;
|
2008-03-20 11:17:50 +00:00
|
|
|
delete cached_quad_list;
|
|
|
|
cached_quad_list = NULL;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// Find out the shape of the window using the XShape extension
|
2007-07-04 09:51:10 +00:00
|
|
|
// or if shape is not set then simply it's the window geometry.
|
2007-04-29 17:35:43 +00:00
|
|
|
QRegion Scene::Window::shape() const
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if (!shape_valid) {
|
|
|
|
Client* c = dynamic_cast< Client* >(toplevel);
|
|
|
|
if (toplevel->shape() || (c != NULL && !c->mask().isEmpty())) {
|
2007-04-29 17:35:43 +00:00
|
|
|
int count, order;
|
2011-01-30 14:34:42 +00:00
|
|
|
XRectangle* rects = XShapeGetRectangles(display(), toplevel->frameId(),
|
|
|
|
ShapeBounding, &count, &order);
|
|
|
|
if (rects) {
|
2007-04-29 17:35:43 +00:00
|
|
|
shape_region = QRegion();
|
2011-01-30 14:34:42 +00:00
|
|
|
for (int i = 0;
|
|
|
|
i < count;
|
|
|
|
++i)
|
|
|
|
shape_region += QRegion(rects[ i ].x, rects[ i ].y,
|
|
|
|
rects[ i ].width, rects[ i ].height);
|
2007-04-29 17:35:43 +00:00
|
|
|
XFree(rects);
|
2008-10-01 11:53:48 +00:00
|
|
|
// make sure the shape is sane (X is async, maybe even XShape is broken)
|
2011-01-30 14:34:42 +00:00
|
|
|
shape_region &= QRegion(0, 0, width(), height());
|
|
|
|
} else
|
2008-03-03 19:17:56 +00:00
|
|
|
shape_region = QRegion();
|
2011-01-30 14:34:42 +00:00
|
|
|
} else
|
|
|
|
shape_region = QRegion(0, 0, width(), height());
|
2007-04-29 17:35:43 +00:00
|
|
|
shape_valid = true;
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
return shape_region;
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2009-05-27 16:04:58 +00:00
|
|
|
QRegion Scene::Window::clientShape() const
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
Client *c = dynamic_cast< Client* >(toplevel);
|
|
|
|
if (c && c->isShade())
|
2009-05-27 16:04:58 +00:00
|
|
|
return QRegion();
|
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
const QRegion r = shape() & QRect(toplevel->clientPos(), toplevel->clientSize());
|
2009-05-27 16:04:58 +00:00
|
|
|
return r.isEmpty() ? QRegion() : r;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2009-04-22 17:29:56 +00:00
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
bool Scene::Window::isVisible() const
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if (dynamic_cast< Deleted* >(toplevel) != NULL)
|
2007-04-29 17:35:43 +00:00
|
|
|
return false;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (!toplevel->isOnCurrentDesktop())
|
2007-04-29 17:35:43 +00:00
|
|
|
return false;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (!toplevel->isOnCurrentActivity())
|
2010-05-15 20:18:57 +00:00
|
|
|
return false;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (Client* c = dynamic_cast< Client* >(toplevel))
|
|
|
|
return c->isShown(true);
|
2007-04-29 17:35:43 +00:00
|
|
|
return true; // Unmanaged is always visible
|
|
|
|
// TODO there may be transformations, so ignore this for now
|
|
|
|
return !toplevel->geometry()
|
2011-01-30 14:34:42 +00:00
|
|
|
.intersected(QRect(0, 0, displayWidth(), displayHeight()))
|
|
|
|
.isEmpty();
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
bool Scene::Window::isOpaque() const
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2007-04-29 17:35:43 +00:00
|
|
|
return toplevel->opacity() == 1.0 && !toplevel->hasAlpha();
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
bool Scene::Window::isPaintingEnabled() const
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2007-04-29 17:35:43 +00:00
|
|
|
return !disable_painting;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
void Scene::Window::resetPaintingEnabled()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2007-04-29 17:35:43 +00:00
|
|
|
disable_painting = 0;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (dynamic_cast< Deleted* >(toplevel) != NULL)
|
2007-04-29 17:35:43 +00:00
|
|
|
disable_painting |= PAINT_DISABLED_BY_DELETE;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (!toplevel->isOnCurrentDesktop())
|
2007-04-29 17:35:43 +00:00
|
|
|
disable_painting |= PAINT_DISABLED_BY_DESKTOP;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (!toplevel->isOnCurrentActivity())
|
2010-05-15 20:18:57 +00:00
|
|
|
disable_painting |= PAINT_DISABLED_BY_ACTIVITY;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (Client* c = dynamic_cast< Client* >(toplevel)) {
|
|
|
|
if (c->isMinimized())
|
2007-04-29 17:35:43 +00:00
|
|
|
disable_painting |= PAINT_DISABLED_BY_MINIMIZE;
|
2012-01-12 06:42:55 +00:00
|
|
|
if (c->tabGroup() && c != c->tabGroup()->current())
|
|
|
|
disable_painting |= PAINT_DISABLED_BY_TAB_GROUP;
|
2011-01-30 14:34:42 +00:00
|
|
|
else if (c->isHiddenInternal())
|
2008-02-23 13:41:53 +00:00
|
|
|
disable_painting |= PAINT_DISABLED;
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
void Scene::Window::enablePainting(int reason)
|
|
|
|
{
|
2007-04-29 17:35:43 +00:00
|
|
|
disable_painting &= ~reason;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
void Scene::Window::disablePainting(int reason)
|
|
|
|
{
|
2007-04-29 17:35:43 +00:00
|
|
|
disable_painting |= reason;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
WindowQuadList Scene::Window::buildQuads(bool force) const
|
|
|
|
{
|
|
|
|
if (cached_quad_list != NULL && !force)
|
2008-03-20 11:17:50 +00:00
|
|
|
return *cached_quad_list;
|
|
|
|
WindowQuadList ret;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (toplevel->clientPos() == QPoint(0, 0) && toplevel->clientSize() == toplevel->visibleRect().size())
|
|
|
|
ret = makeQuads(WindowQuadContents, shape()); // has no decoration
|
|
|
|
else {
|
|
|
|
Client *client = dynamic_cast<Client*>(toplevel);
|
2009-05-27 16:04:58 +00:00
|
|
|
QRegion contents = clientShape();
|
2009-12-01 09:31:47 +00:00
|
|
|
QRegion center = toplevel->transparentRect();
|
2009-06-11 19:42:55 +00:00
|
|
|
QRegion decoration = (client && Workspace::self()->decorationHasAlpha() ?
|
2009-11-25 23:32:35 +00:00
|
|
|
QRegion(client->decorationRect()) : shape()) - center;
|
2011-01-30 14:34:42 +00:00
|
|
|
ret = makeQuads(WindowQuadContents, contents);
|
|
|
|
if (!client || !(center.isEmpty() || client->isShade()))
|
|
|
|
ret += makeQuads(WindowQuadDecoration, decoration);
|
|
|
|
else {
|
2009-07-30 18:22:21 +00:00
|
|
|
// this is a shaded client, we have to create four decoartion quads
|
|
|
|
QRect left, top, right, bottom;
|
2011-01-30 14:34:42 +00:00
|
|
|
client->layoutDecorationRects(left, top, right, bottom, Client::WindowRelative);
|
|
|
|
ret += makeQuads(WindowQuadDecoration, top);
|
|
|
|
ret += makeQuads(WindowQuadDecoration, bottom);
|
|
|
|
ret += makeQuads(WindowQuadDecoration, left);
|
|
|
|
ret += makeQuads(WindowQuadDecoration, right);
|
2008-03-20 11:17:50 +00:00
|
|
|
}
|
2007-07-18 15:01:59 +00:00
|
|
|
}
|
2011-04-03 09:31:33 +00:00
|
|
|
if (m_shadow) {
|
|
|
|
ret << m_shadow->shadowQuads();
|
2011-03-27 10:33:07 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
effects->buildQuads(static_cast<Client*>(toplevel)->effectWindow(), ret);
|
|
|
|
cached_quad_list = new WindowQuadList(ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2007-07-18 15:01:59 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
WindowQuadList Scene::Window::makeQuads(WindowQuadType type, const QRegion& reg) const
|
|
|
|
{
|
2007-07-18 15:01:59 +00:00
|
|
|
WindowQuadList ret;
|
2011-01-30 14:34:42 +00:00
|
|
|
foreach (const QRect & r, reg.rects()) {
|
|
|
|
WindowQuad quad(type);
|
2007-07-18 15:01:59 +00:00
|
|
|
// TODO asi mam spatne pravy dolni roh - bud tady, nebo v jinych castech
|
2011-01-30 14:34:42 +00:00
|
|
|
quad[ 0 ] = WindowVertex(r.x(), r.y(), r.x(), r.y());
|
|
|
|
quad[ 1 ] = WindowVertex(r.x() + r.width(), r.y(), r.x() + r.width(), r.y());
|
|
|
|
quad[ 2 ] = WindowVertex(r.x() + r.width(), r.y() + r.height(), r.x() + r.width(), r.y() + r.height());
|
|
|
|
quad[ 3 ] = WindowVertex(r.x(), r.y() + r.height(), r.x(), r.y() + r.height());
|
|
|
|
ret.append(quad);
|
2007-07-18 15:01:59 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2007-07-18 15:01:59 +00:00
|
|
|
|
2010-07-18 16:32:37 +00:00
|
|
|
//****************************************
|
|
|
|
// Scene::EffectFrame
|
|
|
|
//****************************************
|
|
|
|
Scene::EffectFrame::EffectFrame(EffectFrameImpl* frame)
|
2011-01-30 14:34:42 +00:00
|
|
|
: m_effectFrame(frame)
|
|
|
|
{
|
|
|
|
}
|
2010-07-18 16:32:37 +00:00
|
|
|
|
|
|
|
Scene::EffectFrame::~EffectFrame()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
}
|
2010-07-18 16:32:37 +00:00
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
} // namespace
|