Support for unredirecting fullscreen windows, i.e. games etc. can paint directly

and not be slowed down by going through compositing. Turned on and no UI option
in the naive hope that it won't cause any real problems. Maybe effects doing
window previews should get API to suspend unredirect though.


svn path=/trunk/KDE/kdebase/workspace/; revision=851742
icc-effect-5.14.5
Luboš Luňák 2008-08-24 13:32:57 +00:00
parent 09d81b7e87
commit 14ae8d2dc9
16 changed files with 206 additions and 5 deletions

View File

@ -901,6 +901,7 @@ void Client::internalShow( allowed_t )
map( Allowed );
if( old == Kept )
updateHiddenPreview();
workspace()->checkUnredirect();
}
void Client::internalHide( allowed_t )
@ -915,6 +916,7 @@ void Client::internalHide( allowed_t )
updateHiddenPreview();
addWorkspaceRepaint( geometry());
workspace()->clientHidden( this );
workspace()->checkUnredirect();
}
void Client::internalKeep( allowed_t )
@ -929,6 +931,7 @@ void Client::internalKeep( allowed_t )
updateHiddenPreview();
addWorkspaceRepaint( geometry());
workspace()->clientHidden( this );
workspace()->checkUnredirect();
}
/*!

View File

@ -329,6 +329,7 @@ class Client
protected:
virtual void debug( kdbgstream& stream ) const;
virtual bool shouldUnredirect() const;
private slots:
void pingTimeout();

View File

@ -319,6 +319,10 @@ void Workspace::performCompositing()
// does not actually caused heavy load by firing the timer often too quickly.
if( compositeTimer.interval() != compositeRate )
compositeTimer.start( compositeRate );
// Note: It would seem here we should undo suspended unredirect, but when scenes need
// it for some reason, e.g. transformations or translucency, the next pass that does not
// need this anymore and paints normally will also reset the suspended unredirect.
// Otherwise the window would not be painted normally anyway.
return;
}
// create a list of all windows in the stacking order
@ -335,7 +339,7 @@ void Workspace::performCompositing()
#if 0
// There is a bug somewhere that prevents this from working properly (#160393), but additionally
// this cannot be used so carelessly - needs protections against broken clients, the window
// should not get focus before it's displayed and so on.
// should not get focus before it's displayed, handle unredirected windows properly and so on.
foreach( Toplevel* c, tmp )
if( c->readyForPainting())
windows.append( c );
@ -406,6 +410,8 @@ void Workspace::setupOverlay( Window w )
assert( Extensions::shapeInputAvailable());
XSetWindowBackgroundPixmap( display(), overlay, None );
XShapeCombineRectangles( display(), overlay, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted );
XRectangle rec = { 0, 0, displayWidth(), displayHeight() };
XShapeCombineRectangles( display(), overlay, ShapeBounding, 0, 0, &rec, 1, ShapeSet, Unsorted );
if( w != None )
{
XSetWindowBackgroundPixmap( display(), w, None );
@ -428,6 +434,10 @@ void Workspace::destroyOverlay()
{
if( overlay == None )
return;
// reset the overlay shape
XRectangle rec = { 0, 0, displayWidth(), displayHeight() };
XShapeCombineRectangles( display(), overlay, ShapeBounding, 0, 0, &rec, 1, ShapeSet, Unsorted );
XShapeCombineRectangles( display(), overlay, ShapeInput, 0, 0, &rec, 1, ShapeSet, Unsorted );
#ifdef HAVE_XCOMPOSITE_OVERLAY
XCompositeReleaseOverlayWindow( display(), overlay );
#endif
@ -440,6 +450,58 @@ bool Workspace::compositingActive()
return compositing();
}
// force is needed when the list of windows changes (e.g. a window goes away)
void Workspace::checkUnredirect( bool force )
{
if( !compositing() || overlay == None || !options->unredirectFullscreen )
return;
if( force )
forceUnredirectCheck = true;
if( !unredirectTimer.isActive())
unredirectTimer.start( 0 );
}
void Workspace::delayedCheckUnredirect()
{
ToplevelList list;
bool changed = forceUnredirectCheck;
foreach( Client* c, clients )
list.append( c );
foreach( Unmanaged* c, unmanaged )
list.append( c );
foreach( Toplevel* c, list )
{
if( c->updateUnredirectedState())
changed = true;
}
// no desktops, no Deleted ones
if( !changed )
return;
forceUnredirectCheck = false;
// Cut out parts from the overlay window where unredirected windows are,
// so that they are actually visible.
QRegion reg( 0, 0, displayWidth(), displayHeight());
foreach( Toplevel* c, list )
{
if( c->unredirected())
reg -= c->geometry();
}
QVector< QRect > rects = reg.rects();
XRectangle* xrects = new XRectangle[ rects.count() ];
for( int i = 0;
i < rects.count();
++i )
{
xrects[ i ].x = rects[ i ].x();
xrects[ i ].y = rects[ i ].y();
xrects[ i ].width = rects[ i ].width();
xrects[ i ].height = rects[ i ].height();
}
XShapeCombineRectangles( display(), overlay, ShapeBounding, 0, 0,
xrects, rects.count(), ShapeSet, Unsorted );
delete[] xrects;
}
//****************************************
// Toplevel
//****************************************
@ -455,6 +517,8 @@ void Toplevel::setupCompositing()
damage_region = QRegion( 0, 0, width(), height());
effect_window = new EffectWindowImpl();
effect_window->setWindow( this );
unredirect = false;
workspace()->checkUnredirect( true );
#endif
}
@ -463,6 +527,7 @@ void Toplevel::finishCompositing()
#ifdef KWIN_HAVE_COMPOSITING
if( damage_handle == None )
return;
workspace()->checkUnredirect( true );
if( effect_window->window() == this ) // otherwise it's already passed to Deleted, don't free data
{
discardWindowPixmap();
@ -491,6 +556,8 @@ Pixmap Toplevel::createWindowPixmap()
{
#ifdef KWIN_HAVE_COMPOSITING
assert( compositing());
if( unredirected())
return None;
grabXServer();
KXErrorHandler err;
Pixmap pix = XCompositeNameWindowPixmap( display(), frameId());
@ -641,6 +708,34 @@ void Toplevel::addWorkspaceRepaint( const QRect& r2 )
workspace()->addRepaint( r );
}
bool Toplevel::updateUnredirectedState()
{
assert( compositing());
bool should = shouldUnredirect() && !unredirectSuspend && !shape() && !hasAlpha() && opacity() == 1.0;
if( should && !unredirect )
{
unredirect = true;
kDebug( 1212 ) << "Unredirecting:" << this;
XCompositeUnredirectWindow( display(), frameId(), CompositeRedirectManual );
return true;
}
else if( !should && unredirect )
{
unredirect = false;
kDebug( 1212 ) << "Redirecting:" << this;
XCompositeRedirectWindow( display(), frameId(), CompositeRedirectManual );
discardWindowPixmap();
return true;
}
return false;
}
void Toplevel::suspendUnredirect( bool suspend )
{
unredirectSuspend = suspend;
workspace()->checkUnredirect();
}
//****************************************
// Client
//****************************************
@ -657,4 +752,59 @@ void Client::finishCompositing()
updateVisibility();
}
bool Client::shouldUnredirect() const
{
if( isActiveFullScreen())
{
ToplevelList stacking = workspace()->xStackingOrder();
for( int pos = stacking.count() - 1;
pos >= 0;
--pos )
{
Toplevel* c = stacking.at( pos );
if( c == this ) // is not covered by any other window, ok to unredirect
return true;
if( c->geometry().intersects( geometry()))
return false;
}
abort();
}
return false;
}
//****************************************
// Unmanaged
//****************************************
bool Unmanaged::shouldUnredirect() const
{
// it must cover whole display or one xinerama screen, and be the topmost there
if( geometry() == workspace()->clientArea( FullArea, geometry().center(), workspace()->currentDesktop())
|| geometry() == workspace()->clientArea( ScreenArea, geometry().center(), workspace()->currentDesktop()))
{
ToplevelList stacking = workspace()->xStackingOrder();
for( int pos = stacking.count() - 1;
pos >= 0;
--pos )
{
Toplevel* c = stacking.at( pos );
if( c == this ) // is not covered by any other window, ok to unredirect
return true;
if( c->geometry().intersects( geometry()))
return false;
}
abort();
}
return false;
}
//****************************************
// Deleted
//****************************************
bool Deleted::shouldUnredirect() const
{
return false;
}
} // namespace

View File

@ -41,6 +41,7 @@ class Deleted
virtual QSize clientSize() const;
protected:
virtual void debug( kdbgstream& stream ) const;
virtual bool shouldUnredirect() const;
private:
Deleted( Workspace *ws ); // use create()
void copyToDeleted( Toplevel* c );

View File

@ -1861,6 +1861,7 @@ void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force )
checkMaximizeGeometry();
workspace()->checkActiveScreen( this );
workspace()->updateStackingOrder();
workspace()->checkUnredirect();
if( resized )
{
discardWindowPixmap();
@ -1934,6 +1935,7 @@ void Client::plainResize( int w, int h, ForceGeometry_t force )
checkMaximizeGeometry();
workspace()->checkActiveScreen( this );
workspace()->updateStackingOrder();
workspace()->checkUnredirect();
discardWindowPixmap();
if( scene != NULL )
scene->windowGeometryShapeChanged( this );
@ -1977,6 +1979,7 @@ void Client::move( int x, int y, ForceGeometry_t force )
checkMaximizeGeometry();
workspace()->checkActiveScreen( this );
workspace()->updateStackingOrder();
workspace()->checkUnredirect();
// client itself is not damaged
addWorkspaceRepaint( geom_before_block );
addWorkspaceRepaint( geom ); // trigger repaint of window's new location
@ -2328,6 +2331,7 @@ void Client::setFullScreen( bool set, bool user )
}
}
updateWindowRules();
workspace()->checkUnredirect();
}
int Client::checkFullScreenHack( const QRect& geom ) const

View File

@ -748,6 +748,7 @@ ToplevelList Workspace::xStackingOrder() const
x_stacking.append( c );
if( windows != NULL )
XFree( windows );
const_cast< Workspace* >( this )->checkUnredirect();
return x_stacking;
}

View File

@ -233,6 +233,8 @@ void Options::reloadCompositingSettings(const CompositingPrefs& prefs)
hiddenPreviews = HiddenPreviewsShown;
else if( hps == 1 )
hiddenPreviews = HiddenPreviewsAlways;
unredirectFullscreen = config.readEntry( "UnredirectFullscreen", true );
}

View File

@ -294,6 +294,7 @@ class Options : public KDecorationOptions
bool useCompositing;
CompositingType compositingMode;
HiddenPreviews hiddenPreviews;
bool unredirectFullscreen;
uint refreshRate;
// This is for OpenGL mode

View File

@ -116,7 +116,7 @@ void Scene::paintScreen( int* mask, QRegion* region )
*mask &= ~PAINT_SCREEN_REGION;
*region = infiniteRegion();
}
else if( *mask & PAINT_SCREEN_REGION )
else if( *mask & PAINT_SCREEN_REGION )
{ // make sure not to go outside visible screen
*region &= QRegion( 0, 0, displayWidth(), displayHeight());
}
@ -198,6 +198,9 @@ void Scene::paintGenericScreen( int orig_mask, ScreenPaintData )
if( !w->isPaintingEnabled())
continue;
phase2.append( Phase2Data( w, infiniteRegion(), data.clip, data.mask, data.quads ));
// transformations require window pixmap
w->suspendUnredirect( data.mask
& ( PAINT_WINDOW_TRANSLUCENT | PAINT_SCREEN_TRANSFORMED | PAINT_WINDOW_TRANSFORMED ));
}
foreach( const Phase2Data &d, phase2 )
@ -212,6 +215,7 @@ void Scene::paintSimpleScreen( int orig_mask, QRegion region )
// TODO PAINT_WINDOW_* flags don't belong here, that's why it's in the assert,
// perhaps the two enums should be separated
assert(( orig_mask & ( PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED
| PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS
| PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_OPAQUE )) == 0 );
QHash< Window*, Phase2Data > phase2data;
// Draw each opaque window top to bottom, subtracting the bounding rect of
@ -221,6 +225,7 @@ void Scene::paintSimpleScreen( int orig_mask, QRegion region )
--i )
{
Window* w = stacking_order[ i ];
w->suspendUnredirect( true );
WindowPrePaintData data;
data.mask = orig_mask | ( w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT );
w->resetPaintingEnabled();
@ -233,6 +238,8 @@ void Scene::paintSimpleScreen( int orig_mask, QRegion region )
foreach( const WindowQuad &q, data.quads )
if( q.isTransformed())
kFatal( 1212 ) << "Pre-paint calls are not allowed to transform quads!" ;
if( data.mask & PAINT_WINDOW_TRANSFORMED )
kFatal( 1212 ) << "PAINT_WINDOW_TRANSFORMED without PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS!";
#endif
if( !w->isPaintingEnabled())
continue;
@ -240,6 +247,8 @@ void Scene::paintSimpleScreen( int orig_mask, QRegion region )
painted_region |= data.paint; // make sure it makes it to the screen
// Schedule the window for painting
phase2data[w] = Phase2Data( w, data.paint, data.clip, data.mask, data.quads );
// no transformations, but translucency requires window pixmap
w->suspendUnredirect( data.mask & PAINT_WINDOW_TRANSLUCENT );
}
// Do the actual painting
// First opaque windows, top to bottom

View File

@ -182,6 +182,7 @@ class Scene::Window
void updateToplevel( Toplevel* c );
// creates initial quad list for the window
virtual WindowQuadList buildQuads() const;
void suspendUnredirect( bool suspend );
protected:
WindowQuadList makeQuads( WindowQuadType type, const QRegion& reg ) const;
Toplevel* toplevel;
@ -256,6 +257,12 @@ void Scene::Window::updateToplevel( Toplevel* c )
toplevel = c;
}
inline
void Scene::Window::suspendUnredirect( bool suspend )
{
toplevel->suspendUnredirect( suspend );
}
} // namespace
#endif

View File

@ -261,6 +261,8 @@ void SceneXrender::paintTransformedScreen( int orig_mask )
// The window can clip by its opaque parts the windows below.
region -= w->transformedShape();
}
// translucency or window transformed require window pixmap
w->suspendUnredirect( data.mask & ( PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_TRANSFORMED ));
}
if( !( orig_mask & PAINT_SCREEN_BACKGROUND_FIRST ))
paintBackground( region ); // Fill any areas of the root window not covered by windows

View File

@ -43,6 +43,8 @@ Toplevel::Toplevel( Workspace* ws )
, is_shape( false )
, effect_window( NULL )
, wmClientLeaderWin( 0 )
, unredirect( false )
, unredirectSuspend( false )
{
}

View File

@ -110,6 +110,9 @@ class Toplevel
bool hasAlpha() const;
virtual void setupCompositing();
virtual void finishCompositing();
bool updateUnredirectedState();
bool unredirected() const;
void suspendUnredirect( bool suspend );
void addRepaint( const QRect& r );
void addRepaint( int x, int y, int w, int h );
void addRepaintFull();
@ -144,6 +147,7 @@ class Toplevel
void disownDataPassedToDeleted();
friend kdbgstream& operator<<( kdbgstream& stream, const Toplevel* );
void deleteEffectWindow();
virtual bool shouldUnredirect() const = 0;
QRect geom;
Visual* vis;
int bit_depth;
@ -172,6 +176,8 @@ class Toplevel
QByteArray client_machine;
WId wmClientLeaderWin;
QByteArray window_role;
bool unredirect;
bool unredirectSuspend; // when unredirected, but pixmap is needed temporarily
// when adding new data members, check also copyToDeleted()
};
@ -396,6 +402,11 @@ inline pid_t Toplevel::pid() const
return info->pid();
}
inline bool Toplevel::unredirected() const
{
return unredirect;
}
kdbgstream& operator<<( kdbgstream& stream, const Toplevel* );
kdbgstream& operator<<( kdbgstream& stream, const ToplevelList& );
kdbgstream& operator<<( kdbgstream& stream, const ConstToplevelList& );

View File

@ -43,6 +43,7 @@ class Unmanaged
virtual QSize clientSize() const;
protected:
virtual void debug( kdbgstream& stream ) const;
virtual bool shouldUnredirect() const;
private:
virtual ~Unmanaged(); // use release()
// handlers for X11 events

View File

@ -141,7 +141,8 @@ Workspace::Workspace( bool restore )
overlay_visible( true ),
overlay_shown( false ),
transSlider( NULL ),
transButton( NULL )
transButton( NULL ),
forceUnredirectCheck( true )
{
(void) new KWinAdaptor( this );
QDBusConnection dbus = QDBusConnection::sessionBus();
@ -165,6 +166,8 @@ Workspace::Workspace( bool restore )
connect( &temporaryRulesMessages, SIGNAL( gotMessage( const QString& )),
this, SLOT( gotTemporaryRulesMessage( const QString& )));
connect( &rulesUpdatedTimer, SIGNAL( timeout()), this, SLOT( writeWindowRules()));
connect( &unredirectTimer, SIGNAL( timeout()), this, SLOT( delayedCheckUnredirect()));
unredirectTimer.setSingleShot( true );
updateXTime(); // needed for proper initialization of user_time in Client ctor

View File

@ -194,7 +194,7 @@ class Workspace : public QObject, public KDecorationDefines
* at the last position
*/
const ClientList& stackingOrder() const;
ToplevelList xStackingOrder() const;
ClientList ensureStackingOrder( const ClientList& clients ) const;
Client* topClientOnDesktop( int desktop, int screen, bool unconstrained = false, bool only_normal = true ) const;
@ -330,6 +330,7 @@ class Workspace : public QObject, public KDecorationDefines
// destroys XComposite overlay window
void destroyOverlay();
Window overlayWindow();
void checkUnredirect( bool force = false );
public slots:
void addRepaintFull();
@ -491,6 +492,7 @@ class Workspace : public QObject, public KDecorationDefines
void lostCMSelection();
void updateElectricBorders();
void resetCursorPosTime();
void delayedCheckUnredirect();
protected:
bool keyPressMouseEmulation( XKeyEvent& ev );
@ -518,7 +520,6 @@ class Workspace : public QObject, public KDecorationDefines
bool establishTabBoxGrab();
void removeTabBoxGrab();
ToplevelList xStackingOrder() const;
void propagateClients( bool propagate_new_clients ); // called only from updateStackingOrder
ClientList constrainedStackingOrder();
void raiseClientWithinApplication( Client* c );
@ -740,6 +741,8 @@ class Workspace : public QObject, public KDecorationDefines
bool overlay_shown; // for showOverlay()
QSlider *transSlider;
QPushButton *transButton;
QTimer unredirectTimer;
bool forceUnredirectCheck;
private:
friend bool performTransiencyCheck();