Support for _NET_WM_SYNC_REQUEST, based on a patch

by Rayiner Hashem <gtg990h@mail.gatech.edu>.


svn path=/trunk/KDE/kdebase/workspace/; revision=679986
This commit is contained in:
Luboš Luňák 2007-06-25 08:51:44 +00:00
parent 8eac2caca5
commit 162d6ac7c9
12 changed files with 248 additions and 14 deletions

View file

@ -92,6 +92,12 @@ Atoms::Atoms()
atoms[n] = &kde_net_wm_frame_strut; atoms[n] = &kde_net_wm_frame_strut;
names[n++] = (char*) "_KDE_NET_WM_FRAME_STRUT"; names[n++] = (char*) "_KDE_NET_WM_FRAME_STRUT";
atoms[n] = &net_wm_sync_request_counter;
names[n++] = (char*) "_NET_WM_SYNC_REQUEST_COUNTER";
atoms[n] = &net_wm_sync_request;
names[n++] = (char*) "_NET_WM_SYNC_REQUEST";
assert( n <= max ); assert( n <= max );
XInternAtoms( display(), names, n, false, atoms_return ); XInternAtoms( display(), names, n, false, atoms_return );

View file

@ -47,6 +47,8 @@ class Atoms
Atom xdnd_position; Atom xdnd_position;
Atom net_frame_extents; Atom net_frame_extents;
Atom kde_net_wm_frame_strut; Atom kde_net_wm_frame_strut;
Atom net_wm_sync_request_counter;
Atom net_wm_sync_request;
}; };

View file

@ -36,6 +36,10 @@ License. See the file "COPYING" for the exact licensing terms.
#include <X11/extensions/shape.h> #include <X11/extensions/shape.h>
#include <QX11Info> #include <QX11Info>
#ifdef HAVE_XSYNC
#include <X11/extensions/sync.h>
#endif
// put all externs before the namespace statement to allow the linker // put all externs before the namespace statement to allow the linker
// to resolve them properly // to resolve them properly
@ -91,6 +95,12 @@ Client::Client( Workspace *ws )
block_geometry_updates( 0 ), block_geometry_updates( 0 ),
pending_geometry_update( PendingGeometryNone ), pending_geometry_update( PendingGeometryNone ),
shade_geometry_change( false ), shade_geometry_change( false ),
#ifdef HAVE_XSYNC
sync_counter( None ),
sync_alarm( None ),
#endif
sync_timeout( NULL ),
sync_resize_pending( false ),
border_left( 0 ), border_left( 0 ),
border_right( 0 ), border_right( 0 ),
border_top( 0 ), border_top( 0 ),
@ -147,6 +157,9 @@ Client::Client( Workspace *ws )
geom = QRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0) geom = QRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0)
client_size = QSize( 100, 100 ); client_size = QSize( 100, 100 );
#if defined(HAVE_XSYNC) || defined(HAVE_XDAMAGE)
ready_for_painting = false; // wait for first damage or sync reply
#endif
// SELI initialize xsizehints?? // SELI initialize xsizehints??
} }
@ -156,6 +169,10 @@ Client::Client( Workspace *ws )
*/ */
Client::~Client() Client::~Client()
{ {
#ifdef HAVE_XSYNC
if( sync_alarm != None )
XSyncDestroyAlarm( display(), sync_alarm );
#endif
assert(!moveResizeMode); assert(!moveResizeMode);
assert( client == None ); assert( client == None );
assert( wrapper == None ); assert( wrapper == None );
@ -1431,6 +1448,76 @@ void Client::getWindowProtocols()
} }
} }
void Client::getSyncCounter()
{
#ifdef HAVE_XSYNC
if( !Extensions::syncAvailable())
return;
Atom retType;
unsigned long nItemRet;
unsigned long byteRet;
int formatRet;
unsigned char* propRet;
int ret = XGetWindowProperty( display(), window(), atoms->net_wm_sync_request_counter,
0, 1, false, XA_CARDINAL, &retType, &formatRet, &nItemRet, &byteRet, &propRet );
if( ret == Success && formatRet == 32 )
{
sync_counter = *(long*)propRet;
XSyncIntToValue( &sync_counter_value, 0 );
XSyncValue zero;
XSyncIntToValue( &zero, 0 );
XSyncSetCounter( display(), sync_counter, zero );
if( sync_alarm == None )
{
XSyncAlarmAttributes attrs;
attrs.trigger.counter = sync_counter;
attrs.trigger.value_type = XSyncRelative;
attrs.trigger.test_type = XSyncPositiveTransition;
XSyncIntToValue( &attrs.trigger.wait_value, 1 );
XSyncIntToValue( &attrs.delta, 1 );
sync_alarm = XSyncCreateAlarm( display(),
XSyncCACounter | XSyncCAValueType | XSyncCATestType | XSyncCADelta | XSyncCAValue,
&attrs );
}
}
#endif
}
// send the client a _NET_SYNC_REQUEST
void Client::sendSyncRequest()
{
#ifdef HAVE_XSYNC
if( sync_counter == None )
return;
// we increment before the notify so that after the notify
// syncCounterSerial will equal the value we are expecting
// in the acknowledgement
int overflow;
XSyncValue one;
XSyncIntToValue( &one, 1 );
#undef XSyncValueAdd // it causes a warning :-/
XSyncValueAdd( &sync_counter_value, sync_counter_value, one, &overflow );
// send the message to client
XEvent ev;
ev.xclient.type = ClientMessage;
ev.xclient.window = window();
ev.xclient.format = 32;
ev.xclient.message_type = atoms->wm_protocols;
ev.xclient.data.l[ 0 ] = atoms->net_wm_sync_request;
ev.xclient.data.l[ 1 ] = xTime();
ev.xclient.data.l[ 2 ] = XSyncValueLow32( sync_counter_value );
ev.xclient.data.l[ 3 ] = XSyncValueHigh32( sync_counter_value );
ev.xclient.data.l[ 4 ] = 0;
XSendEvent( display(), window(), False, NoEventMask, &ev );
XSync(display(),false);
#endif
}
bool Client::wantsTabFocus() const bool Client::wantsTabFocus() const
{ {
return ( isNormalWindow() || isDialog()) && wantsInput(); return ( isNormalWindow() || isDialog()) && wantsInput();

View file

@ -12,6 +12,8 @@ License. See the file "COPYING" for the exact licensing terms.
#ifndef KWIN_CLIENT_H #ifndef KWIN_CLIENT_H
#define KWIN_CLIENT_H #define KWIN_CLIENT_H
#include <config-X11.h>
#include <qframe.h> #include <qframe.h>
#include <QPixmap> #include <QPixmap>
#include <netwm.h> #include <netwm.h>
@ -30,6 +32,10 @@ License. See the file "COPYING" for the exact licensing terms.
#include "rules.h" #include "rules.h"
#include "toplevel.h" #include "toplevel.h"
#ifdef HAVE_XSYNC
#include <X11/extensions/sync.h>
#endif
class QTimer; class QTimer;
class K3Process; class K3Process;
class KStartupInfoData; class KStartupInfoData;
@ -84,6 +90,9 @@ class Client
bool windowEvent( XEvent* e ); bool windowEvent( XEvent* e );
virtual bool eventFilter( QObject* o, QEvent* e ); virtual bool eventFilter( QObject* o, QEvent* e );
#ifdef HAVE_XSYNC
void syncEvent( XSyncAlarmNotifyEvent* e );
#endif
bool manage( Window w, bool isMapped ); bool manage( Window w, bool isMapped );
void releaseWindow( bool on_shutdown = false ); void releaseWindow( bool on_shutdown = false );
@ -293,6 +302,9 @@ class Client
void leaveNotifyEvent( XCrossingEvent* e ); void leaveNotifyEvent( XCrossingEvent* e );
void focusInEvent( XFocusInEvent* e ); void focusInEvent( XFocusInEvent* e );
void focusOutEvent( XFocusOutEvent* e ); void focusOutEvent( XFocusOutEvent* e );
#ifdef HAVE_XDAMAGE
virtual void damageNotifyEvent( XDamageNotifyEvent* e );
#endif
bool buttonPressEvent( Window w, int button, int state, int x, int y, int x_root, int y_root ); bool buttonPressEvent( Window w, int button, int state, int x, int y, int x_root, int y_root );
bool buttonReleaseEvent( Window w, int button, int state, int x, int y, int x_root, int y_root ); bool buttonReleaseEvent( Window w, int button, int state, int x, int y, int x_root, int y_root );
@ -307,6 +319,7 @@ class Client
void pingTimeout(); void pingTimeout();
void processKillerExited(); void processKillerExited();
void demandAttentionKNotify(); void demandAttentionKNotify();
void syncTimeout();
private: private:
// ICCCM 4.1.3.1, 4.1.4 , NETWM 2.5.1 // ICCCM 4.1.3.1, 4.1.4 , NETWM 2.5.1
@ -339,6 +352,8 @@ class Client
NETExtendedStrut strut() const; NETExtendedStrut strut() const;
int checkShadeGeometry( int w, int h ); int checkShadeGeometry( int w, int h );
void blockGeometryUpdates( bool block ); void blockGeometryUpdates( bool block );
void getSyncCounter();
void sendSyncRequest();
bool startMoveResize(); bool startMoveResize();
void finishMoveResize( bool cancel ); void finishMoveResize( bool cancel );
@ -352,6 +367,7 @@ class Client
void ungrabButton( int mod ); void ungrabButton( int mod );
void resetMaximize(); void resetMaximize();
void resizeDecoration( const QSize& s ); void resizeDecoration( const QSize& s );
void performMoveResize();
void pingWindow(); void pingWindow();
void killProcess( bool ask, Time timestamp = CurrentTime ); void killProcess( bool ask, Time timestamp = CurrentTime );
@ -465,6 +481,13 @@ class Client
PendingGeometry_t pending_geometry_update; PendingGeometry_t pending_geometry_update;
QRect geom_before_block; QRect geom_before_block;
bool shade_geometry_change; bool shade_geometry_change;
#ifdef HAVE_XSYNC
XSyncCounter sync_counter;
XSyncValue sync_counter_value;
XSyncAlarm sync_alarm;
#endif
QTimer* sync_timeout;
bool sync_resize_pending;
int border_left, border_right, border_top, border_bottom; int border_left, border_right, border_top, border_bottom;
QRegion _mask; QRegion _mask;
static bool check_active_modal; // see Client::checkActiveModal() static bool check_active_modal; // see Client::checkActiveModal()

View file

@ -284,6 +284,11 @@ void Workspace::performCompositing()
repaints_region |= c->repaints().translated( c->pos()); repaints_region |= c->repaints().translated( c->pos());
c->resetRepaints( c->rect()); c->resetRepaints( c->rect());
} }
ToplevelList tmp = windows;
windows.clear();
foreach( Toplevel* c, tmp )
if( c->readyForPainting())
windows.append( c );
QRegion repaints = repaints_region; QRegion repaints = repaints_region;
// clear all repaints, so that post-pass can add repaints for the next repaint // clear all repaints, so that post-pass can add repaints for the next repaint
repaints_region = QRegion(); repaints_region = QRegion();
@ -445,6 +450,17 @@ void Toplevel::damageNotifyEvent( XDamageNotifyEvent* e )
break; break;
} }
} }
void Client::damageNotifyEvent( XDamageNotifyEvent* e )
{
Toplevel::damageNotifyEvent( e );
#ifdef HAVE_XSYNC
if( sync_counter == None ) // cannot detect complete redraw, consider done now
ready_for_painting = true;
#else
ready_for_painting = true; // no sync at all, consider done now
#endif
}
#endif #endif
void Toplevel::addDamage( const QRect& r ) void Toplevel::addDamage( const QRect& r )

View file

@ -478,6 +478,15 @@ bool Workspace::workspaceEvent( XEvent * e )
QTimer::singleShot( 0, this, SLOT( setupCompositing() ) ); QTimer::singleShot( 0, this, SLOT( setupCompositing() ) );
} }
} }
else if( e->type == Extensions::syncAlarmNotifyEvent() && Extensions::syncAvailable())
{
#ifdef HAVE_XSYNC
foreach( Client* c, clients )
c->syncEvent( reinterpret_cast< XSyncAlarmNotifyEvent* >( e ));
foreach( Client* c, desktops )
c->syncEvent( reinterpret_cast< XSyncAlarmNotifyEvent* >( e ));
#endif
}
break; break;
} }
return false; return false;
@ -909,6 +918,8 @@ void Client::propertyNotifyEvent( XPropertyEvent* e )
getWindowProtocols(); getWindowProtocols();
else if( e->atom == atoms->motif_wm_hints ) else if( e->atom == atoms->motif_wm_hints )
getMotifHints(); getMotifHints();
else if( e->atom == atoms->net_wm_sync_request_counter )
getSyncCounter();
break; break;
} }
} }
@ -1560,6 +1571,23 @@ void Client::keyPressEvent( uint key_code )
QCursor::setPos( pos ); QCursor::setPos( pos );
} }
#ifdef HAVE_XSYNC
void Client::syncEvent( XSyncAlarmNotifyEvent* e )
{
if( e->alarm == sync_alarm && XSyncValueEqual( e->counter_value, sync_counter_value ))
{
ready_for_painting = true;
if( isResize())
{
delete sync_timeout;
sync_timeout = NULL;
if( sync_resize_pending )
performMoveResize();
}
}
}
#endif
// **************************************** // ****************************************
// Unmanaged // Unmanaged
// **************************************** // ****************************************

View file

@ -2368,6 +2368,8 @@ void Client::leaveMoveResize()
moveResizeMode = false; moveResizeMode = false;
delete eater; delete eater;
eater = 0; eater = 0;
delete sync_timeout;
sync_timeout = NULL;
if( options->electricBorders() == Options::ElectricMoveOnly ) if( options->electricBorders() == Options::ElectricMoveOnly )
workspace()->reserveElectricBorderSwitching( false ); workspace()->reserveElectricBorderSwitching( false );
} }
@ -2618,26 +2620,57 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root )
else else
assert( false ); assert( false );
if( update ) if( isResize())
{ {
if( rules()->checkMoveResizeMode if( sync_timeout != NULL )
( isResize() ? options->resizeMode : options->moveMode ) == Options::Opaque )
{ {
setGeometry( moveResizeGeom ); sync_resize_pending = true;
positionGeometryTip(); return;
} }
else if( rules()->checkMoveResizeMode
( isResize() ? options->resizeMode : options->moveMode ) == Options::Transparent )
{
clearbound(); // it's necessary to move the geometry tip when there's no outline
positionGeometryTip(); // shown, otherwise it would cause repaint problems in case
drawbound( moveResizeGeom ); // they overlap; the paint event will come after this,
} // so the geometry tip will be painted above the outline
} }
if( update )
performMoveResize();
if ( isMove() ) if ( isMove() )
workspace()->checkElectricBorder(globalPos, xTime()); workspace()->checkElectricBorder(globalPos, xTime());
}
void Client::performMoveResize()
{
#ifdef HAVE_XSYNC
if( isResize() && sync_counter != None )
{
sync_timeout = new QTimer( this );
connect( sync_timeout, SIGNAL( timeout()), SLOT( syncTimeout()));
sync_timeout->setSingleShot( true );
sync_timeout->start( 500 );
sendSyncRequest();
}
#endif
sync_resize_pending = false;
if( rules()->checkMoveResizeMode
( isResize() ? options->resizeMode : options->moveMode ) == Options::Opaque )
{
setGeometry( moveResizeGeom );
positionGeometryTip();
}
else if( rules()->checkMoveResizeMode
( isResize() ? options->resizeMode : options->moveMode ) == Options::Transparent )
{
clearbound(); // it's necessary to move the geometry tip when there's no outline
positionGeometryTip(); // shown, otherwise it would cause repaint problems in case
drawbound( moveResizeGeom ); // they overlap; the paint event will come after this,
} // so the geometry tip will be painted above the outline
if( effects ) if( effects )
static_cast<EffectsHandlerImpl*>(effects)->windowUserMovedResized( effectWindow(), false, false ); static_cast<EffectsHandlerImpl*>(effects)->windowUserMovedResized( effectWindow(), false, false );
} }
void Client::syncTimeout()
{
sync_timeout->deleteLater();
sync_timeout = NULL;
if( sync_resize_pending )
performMoveResize();
}
} // namespace } // namespace

View file

@ -96,6 +96,7 @@ bool Client::manage( Window w, bool isMapped )
getWindowRole(); getWindowRole();
getWmClientLeader(); getWmClientLeader();
getWmClientMachine(); getWmClientMachine();
getSyncCounter();
// first only read the caption text, so that setupWindowRules() can use it for matching, // first only read the caption text, so that setupWindowRules() can use it for matching,
// and only then really set the caption using setCaption(), which checks for duplicates etc. // and only then really set the caption using setCaption(), which checks for duplicates etc.
// and also relies on rules already existing // and also relies on rules already existing
@ -441,6 +442,9 @@ bool Client::manage( Window w, bool isMapped )
workspace()->restoreSessionStackingOrder( this ); workspace()->restoreSessionStackingOrder( this );
} }
if( compositing()) // sending ConfigureNotify is done when setting mapping state below,
sendSyncRequest(); // getting the first sync response means window is ready for compositing
if( isShown( true ) && !doNotShow ) if( isShown( true ) && !doNotShow )
{ {
if( isDialog()) if( isDialog())

View file

@ -22,6 +22,7 @@ namespace KWin
Toplevel::Toplevel( Workspace* ws ) Toplevel::Toplevel( Workspace* ws )
: vis( NULL ) : vis( NULL )
, info( NULL ) , info( NULL )
, ready_for_painting( true )
, client( None ) , client( None )
, frame( None ) , frame( None )
, wspace( ws ) , wspace( ws )
@ -104,6 +105,7 @@ void Toplevel::copyToDeleted( Toplevel* c )
frame = c->frame; frame = c->frame;
wspace = c->wspace; wspace = c->wspace;
window_pix = c->window_pix; window_pix = c->window_pix;
ready_for_painting = c->ready_for_painting;
#ifdef HAVE_XDAMAGE #ifdef HAVE_XDAMAGE
damage_handle = None; damage_handle = None;
#endif #endif

View file

@ -85,6 +85,7 @@ class Toplevel
static bool resourceMatch( const Toplevel* c1, const Toplevel* c2 ); static bool resourceMatch( const Toplevel* c1, const Toplevel* c2 );
Pixmap windowPixmap( bool allow_create = true ); // may return None (e.g. at a bad moment while resizing) Pixmap windowPixmap( bool allow_create = true ); // may return None (e.g. at a bad moment while resizing)
bool readyForPainting() const; // true if the window has been already painted its contents
Visual* visual() const; Visual* visual() const;
bool shape() const; bool shape() const;
void setOpacity( double opacity ); void setOpacity( double opacity );
@ -111,7 +112,7 @@ class Toplevel
void detectShape( Window id ); void detectShape( Window id );
virtual void propertyNotifyEvent( XPropertyEvent* e ); virtual void propertyNotifyEvent( XPropertyEvent* e );
#ifdef HAVE_XDAMAGE #ifdef HAVE_XDAMAGE
void damageNotifyEvent( XDamageNotifyEvent* e ); virtual void damageNotifyEvent( XDamageNotifyEvent* e );
#endif #endif
Pixmap createWindowPixmap(); Pixmap createWindowPixmap();
void discardWindowPixmap(); void discardWindowPixmap();
@ -130,6 +131,7 @@ class Toplevel
Visual* vis; Visual* vis;
int bit_depth; int bit_depth;
NETWinInfo* info; NETWinInfo* info;
bool ready_for_painting;
private: private:
static QByteArray staticWindowRole(WId); static QByteArray staticWindowRole(WId);
static QByteArray staticSessionId(WId); static QByteArray staticSessionId(WId);
@ -219,6 +221,11 @@ inline QRect Toplevel::rect() const
return QRect( 0, 0, width(), height()); return QRect( 0, 0, width(), height());
} }
inline bool Toplevel::readyForPainting() const
{
return ready_for_painting;
}
inline Visual* Toplevel::visual() const inline Visual* Toplevel::visual() const
{ {
return vis; return vis;

View file

@ -50,6 +50,9 @@ License. See the file "COPYING" for the exact licensing terms.
#ifdef HAVE_OPENGL #ifdef HAVE_OPENGL
#include <GL/glx.h> #include <GL/glx.h>
#endif #endif
#ifdef HAVE_XSYNC
#include <X11/extensions/sync.h>
#endif
#include <stdio.h> #include <stdio.h>
@ -74,6 +77,8 @@ int Extensions::composite_version = 0;
int Extensions::fixes_version = 0; int Extensions::fixes_version = 0;
int Extensions::render_version = 0; int Extensions::render_version = 0;
bool Extensions::has_glx = false; bool Extensions::has_glx = false;
bool Extensions::has_sync = false;
int Extensions::sync_event_base = 0;
void Extensions::init() void Extensions::init()
{ {
@ -131,6 +136,14 @@ void Extensions::init()
has_glx = false; has_glx = false;
#ifdef HAVE_OPENGL #ifdef HAVE_OPENGL
has_glx = glXQueryExtension( display(), &dummy, &dummy ); has_glx = glXQueryExtension( display(), &dummy, &dummy );
#endif
#ifdef HAVE_XSYNC
if( XSyncQueryExtension( display(), &sync_event_base, &dummy ))
{
int major = 0, minor = 0;
if( XSyncInitialize( display(), &major, &minor ))
has_sync = true;
}
#endif #endif
kDebug( 1212 ) << "Extensions: shape: 0x" << QString::number( shape_version, 16 ) kDebug( 1212 ) << "Extensions: shape: 0x" << QString::number( shape_version, 16 )
<< " composite: 0x" << QString::number( composite_version, 16 ) << " composite: 0x" << QString::number( composite_version, 16 )
@ -190,6 +203,15 @@ bool Extensions::fixesRegionAvailable()
return fixes_version >= 0x30; // 3 return fixes_version >= 0x30; // 3
} }
int Extensions::syncAlarmNotifyEvent()
{
#ifdef HAVE_XSYNC
return sync_event_base + XSyncAlarmNotify;
#else
return 0;
#endif
}
void Motif::readFlags( WId w, bool& noborder, bool& resize, bool& move, void Motif::readFlags( WId w, bool& noborder, bool& resize, bool& move,
bool& minimize, bool& maximize, bool& close ) bool& minimize, bool& maximize, bool& close )
{ {

View file

@ -143,6 +143,8 @@ class Extensions
static bool fixesAvailable() { return fixes_version > 0; } static bool fixesAvailable() { return fixes_version > 0; }
static bool fixesRegionAvailable(); static bool fixesRegionAvailable();
static bool glxAvailable() { return has_glx; } static bool glxAvailable() { return has_glx; }
static bool syncAvailable() { return has_sync; }
static int syncAlarmNotifyEvent();
private: private:
static int shape_version; static int shape_version;
static int shape_event_base; static int shape_event_base;
@ -154,6 +156,8 @@ class Extensions
static int render_version; static int render_version;
static int fixes_version; static int fixes_version;
static bool has_glx; static bool has_glx;
static bool has_sync;
static int sync_event_base;
}; };
// compile with XShape older than 1.0 // compile with XShape older than 1.0