kwin: implement _KDE_NET_WM_OPAQUE_REGION

This patch implements an XProperty named _KDE_NET_WM_OPAQUE_REGION
which gives the compositor the information which part of a window
is opaque although it is an ARGB visual. The basic ideas are from
http://www.mail-archive.com/wm-spec-list@gnome.org/msg00715.html

Additionally the patch makes kwin  use this information to do a better
clipping in Scene::paintSimpleScreen which should result in a higher
performance.

REVIEW: 102933
This commit is contained in:
Philipp Knechtges 2011-10-22 11:02:49 +02:00
parent f76c40c424
commit 2c08a14ff4
8 changed files with 81 additions and 3 deletions

View file

@ -120,6 +120,9 @@ Atoms::Atoms()
atoms[n] = &kde_net_wm_shadow;
names[n++] = (char*) "_KDE_NET_WM_SHADOW";
atoms[n] = &kde_net_wm_opaque_region;
names[n++] = (char*) "_KDE_NET_WM_OPAQUE_REGION";
assert(n <= max);
XInternAtoms(display(), names, n, false, atoms_return);

View file

@ -62,6 +62,7 @@ public:
Atom net_wm_sync_request;
Atom kde_net_wm_block_compositing;
Atom kde_net_wm_shadow;
Atom kde_net_wm_opaque_region;
};

View file

@ -1648,6 +1648,8 @@ void Toplevel::propertyNotifyEvent(XPropertyEvent* e)
getWindowRole();
else if (e->atom == atoms->kde_net_wm_shadow)
getShadow();
else if (e->atom == atoms->kde_net_wm_opaque_region)
getWmOpaqueRegion();
break;
}
emit propertyNotify(this, e->atom);

View file

@ -136,6 +136,7 @@ bool Client::manage(Window w, bool isMapped)
getWindowProtocols();
getWmNormalHints(); // Get xSizeHint
getMotifHints();
getWmOpaqueRegion();
// TODO: Try to obey all state information from info->state()

View file

@ -273,7 +273,15 @@ void Scene::paintSimpleScreen(int orig_mask, QRegion region)
topw->resetRepaints(topw->shadow()->shadowRegion().boundingRect());
}
// Clip out the decoration for opaque windows; the decoration is drawn in the second pass
data.clip = w->isOpaque() ? w->clientShape().translated(w->x(), w->y()) : QRegion();
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
data.clip = (w->clientShape() & topw->opaqueRegion()).translated(w->x(), w->y());
} else {
data.clip = QRegion();
}
data.quads = w->buildQuads();
// preparation step
effects->prePaintWindow(effectWindow(w), data, time_diff);
@ -314,7 +322,9 @@ void Scene::paintSimpleScreen(int orig_mask, QRegion region)
// a higher opaque window
data->region -= allclips;
if (!(data->mask & PAINT_WINDOW_TRANSLUCENT)) {
// Here we rely on WindowPrePaintData::setTranslucent() to remove
// the clip if needed.
if (!data->clip.isEmpty()) {
// clip away this region for all windows below this one
allclips |= data->clip;
// Paint the opaque window
@ -326,7 +336,8 @@ void Scene::paintSimpleScreen(int orig_mask, QRegion region)
data->region -= data->clip;
// if prePaintWindow didn't change the clipping area we only have to paint
// the decoration
if (data-> clip == data.key()->clientShape().translated(data.key()->x(), data.key()->y())) {
if ((data-> clip ^
data.key()->clientShape().translated(data.key()->x(), data.key()->y())).isEmpty()) {
data->mask |= PAINT_DECORATION_ONLY;
}
}

View file

@ -131,6 +131,7 @@ void Toplevel::copyToDeleted(Toplevel* c)
client_machine = c->wmClientMachine(false);
wmClientLeaderWin = c->wmClientLeader();
window_role = c->windowRole();
opaque_region = c->opaqueRegion();
// this needs to be done already here, otherwise 'c' could very likely
// call discardWindowPixmap() in something called during cleanup
c->window_pix = None;
@ -396,6 +397,44 @@ const Shadow *Toplevel::shadow() const
}
}
void Toplevel::getWmOpaqueRegion()
{
const int length=32768;
unsigned long bytes_after_return=0;
QRegion new_opaque_region;
do {
unsigned long* data;
Atom type;
int rformat;
unsigned long nitems;
if (XGetWindowProperty(display(), client,
atoms->kde_net_wm_opaque_region, 0, length, false, XA_CARDINAL,
&type, &rformat, &nitems, &bytes_after_return,
reinterpret_cast< unsigned char** >(&data)) == Success) {
if (type != XA_CARDINAL || rformat != 32 || nitems%4) {
// it can happen, that the window does not provide this property
XFree(data);
break;
}
for (unsigned int i = 0; i < nitems;) {
const int x = data[i++];
const int y = data[i++];
const int w = data[i++];
const int h = data[i++];
new_opaque_region += QRect(x,y,w,h);
}
XFree(data);
} else {
kWarning(1212) << "XGetWindowProperty failed";
break;
}
} while (bytes_after_return > 0);
opaque_region = new_opaque_region;
}
} // namespace
#include "toplevel.moc"

View file

@ -156,6 +156,13 @@ public:
**/
void getShadow();
/**
* This method returns the area that the Toplevel window reports to be opaque.
* It is supposed to only provide valueable information if @link hasAlpha is @c true .
* @see hasAlpha
**/
const QRegion& opaqueRegion() const;
signals:
void opacityChanged(KWin::Toplevel* toplevel, qreal oldOpacity);
void damaged(KWin::Toplevel* toplevel, const QRect& damage);
@ -177,6 +184,13 @@ protected:
void addDamageFull();
void getWmClientLeader();
void getWmClientMachine();
/**
* This function fetches the opaque region from this Toplevel.
* Will only be called on corresponding property changes and for initialization.
**/
void getWmOpaqueRegion();
void getResourceClass();
void getWindowRole();
virtual void debug(QDebug& stream) const = 0;
@ -214,6 +228,7 @@ private:
QByteArray window_role;
bool unredirect;
bool unredirectSuspend; // when unredirected, but pixmap is needed temporarily
QRegion opaque_region;
// when adding new data members, check also copyToDeleted()
};
@ -397,6 +412,11 @@ inline bool Toplevel::hasAlpha() const
return depth() == 32;
}
inline const QRegion& Toplevel::opaqueRegion() const
{
return opaque_region;
}
inline
EffectWindowImpl* Toplevel::effectWindow()
{

View file

@ -72,6 +72,7 @@ bool Unmanaged::track(Window w)
if (Extensions::shapeAvailable())
XShapeSelectInput(display(), w, ShapeNotifyMask);
detectShape(w);
getWmOpaqueRegion();
setupCompositing();
ungrabXServer();
if (effects)