Blob Blame History Raw
qt-bugs@ issue : none
bugs.kde.org number : none
applied: no
author: Lubos Lunak <l.lunak@kde.org>

Support for _NET_WM_SYNC_REQUEST - allows the WM to find out when the app
finished one redraw - less flicker during resize and with compositing
also when opening a window.

--- src/kernel/qwidget.h.sav	2007-06-22 14:14:05.000000000 +0200
+++ src/kernel/qwidget.h	2007-06-23 11:53:39.000000000 +0200
@@ -586,6 +586,14 @@ private:
     void	 destroyInputContext();
     void	 focusInputContext();
     void	 checkChildrenDnd();
+
+#ifndef QT_NO_XSYNC
+    void        createSyncCounter();
+    void        destroySyncCounter();
+    void        incrementSyncCounter();
+    void        handleSyncRequest( void* ev );
+#endif
+
 #elif defined(Q_WS_MAC)
     uint    own_id : 1, macDropEnabled : 1;
     EventHandlerRef window_event;
@@ -962,8 +970,12 @@ struct Q_EXPORT QTLWExtra {
     uint     uspos : 1;				// User defined position
     uint     ussize : 1;			// User defined size
 #if defined(QT_NO_IM_EXTENSIONS)
     void    *xic;				// Input Context
 #endif
+#ifndef QT_NO_XSYNC
+    ulong    syncCounter;
+    uint     syncRequestValue[2];
+#endif
 #endif
 #if defined(Q_WS_MAC)
     WindowGroupRef group;
--- src/kernel/qt_x11_p.h.sav	2007-02-23 14:01:18.000000000 +0100
+++ src/kernel/qt_x11_p.h	2007-06-23 11:53:39.000000000 +0200
@@ -174,6 +174,11 @@ extern "C" {
 #endif // QT_NO_XRENDER
 
 
+#ifndef QT_NO_XSYNC
+#  include <X11/extensions/sync.h>
+#endif // QT_NO_XSYNC
+
+
 #ifndef QT_NO_XKB
 #  include <X11/XKBlib.h>
 #endif // QT_NO_XKB
--- src/kernel/qwidget_x11.cpp.sav	2007-04-16 13:47:26.000000000 +0200
+++ src/kernel/qwidget_x11.cpp	2007-06-23 19:48:44.000000000 +0200
@@ -87,6 +87,12 @@ static QWidget *keyboardGrb = 0;
 extern Time qt_x_time;
 extern Time qt_x_user_time;
 
+#ifndef QT_NO_XSYNC
+extern Atom qt_net_wm_sync_request_counter;
+extern Atom qt_net_wm_sync_request;
+extern bool qt_use_xsync;
+#endif
+
 // defined in qfont_x11.cpp
 extern bool qt_has_xft;
 
@@ -595,11 +601,14 @@ void QWidget::create( WId window, bool i
 
 	XResizeWindow( dpy, id, crect.width(), crect.height() );
 	XStoreName( dpy, id, qAppName() );
-	Atom protocols[4];
+	Atom protocols[5];
 	int n = 0;
 	protocols[n++] = qt_wm_delete_window;	// support del window protocol
 	protocols[n++] = qt_wm_take_focus;	// support take focus window protocol
 	protocols[n++] = qt_net_wm_ping;	// support _NET_WM_PING protocol
+#ifndef QT_NO_XSYNC
+	protocols[n++] = qt_net_wm_sync_request;// support the _NET_WM_SYNC_REQUEST protocol
+#endif
 	if ( testWFlags( WStyle_ContextHelp ) )
 	    protocols[n++] = qt_net_wm_context_help;
 	XSetWMProtocols( dpy, id, protocols, n );
@@ -625,6 +634,14 @@ void QWidget::create( WId window, bool i
 	XChangeProperty(dpy, id, qt_net_wm_pid, XA_CARDINAL, 32, PropModeReplace,
 			(unsigned char *) &curr_pid, 1);
 
+#ifndef QT_NO_XSYNC
+        // set _NET_WM_SYNC_COUNTER
+        createSyncCounter();
+        long counterVal = topData()->syncCounter;
+        XChangeProperty( dpy, id, qt_net_wm_sync_request_counter, XA_CARDINAL, 32, PropModeReplace,
+                         (unsigned char*) &counterVal, 1);
+#endif
+
 	// when we create a toplevel widget, the frame strut should be dirty
 	fstrut_dirty = 1;
 
@@ -720,6 +737,9 @@ void QWidget::destroy( bool destroyWindo
 	    if ( destroyWindow )
 		qt_XDestroyWindow( this, x11Display(), winid );
 	}
+#ifndef QT_NO_XSYNC
+        destroySyncCounter();
+#endif
 	setWinId( 0 );
 
 	extern void qPRCleanup( QWidget *widget ); // from qapplication_x11.cpp
@@ -769,6 +789,10 @@ void QWidget::reparentSys( QWidget *pare
         destroyInputContext();
     }
 
+#ifndef QT_NO_XSYNC
+    destroySyncCounter();
+#endif
+
     if ( isTopLevel() || !parent ) // we are toplevel, or reparenting to toplevel
         topData()->parentWinId = 0;
 
@@ -2456,8 +2480,13 @@ void QWidget::createTLSysExtra()
 {
 #if defined(QT_NO_IM_EXTENSIONS)
     // created lazily
     extra->topextra->xic = 0;
 #endif
+#ifndef QT_NO_XSYNC
+    extra->topextra->syncCounter = 0;
+    extra->topextra->syncRequestValue[0] = 0;
+    extra->topextra->syncRequestValue[1] = 0;
+#endif
 }
 
 void QWidget::deleteTLSysExtra()
@@ -2501,6 +2530,51 @@ void QWidget::checkChildrenDnd()
     }
 }
 
+
+#ifndef QT_NO_XSYNC
+// create a window's XSyncCounter
+void QWidget::createSyncCounter()
+{
+    if( !qt_use_xsync || !isTopLevel() || topData()->syncCounter )
+        return;
+    XSyncValue zero;
+    XSyncIntToValue( &zero, 0 );
+    topData()->syncCounter = XSyncCreateCounter( x11Display(), zero );
+}
+
+// destroy a window's XSyncCounter
+void QWidget::destroySyncCounter()
+{
+    if( !qt_use_xsync || !extra || !extra->topextra
+        || !extra->topextra->syncCounter )
+        return;
+    XSyncDestroyCounter( x11Display(), extra->topextra->syncCounter );
+    extra->topextra->syncCounter = 0;
+}
+
+// increment a window's XSyncCounter
+void QWidget::incrementSyncCounter()
+{
+    if( qt_use_xsync && topData()->syncCounter &&
+        !(topData()->syncRequestValue[0] == 0 &&
+         topData()->syncRequestValue[1] == 0) ) {
+        XSyncValue val;
+        XSyncIntsToValue( &val, topData()->syncRequestValue[ 0 ], topData()->syncRequestValue[ 1 ] );
+        XSyncSetCounter( x11Display(), topData()->syncCounter, val );
+        topData()->syncRequestValue[0] = topData()->syncRequestValue[1] = 0;
+    }
+}
+
+// handle _NET_WM_SYNC_REQUEST
+void QWidget::handleSyncRequest( void* ev )
+{
+    XEvent* xev = (XEvent*)ev;
+    topData()->syncRequestValue[ 0 ] = xev->xclient.data.l[ 2 ];
+    topData()->syncRequestValue[ 1 ] = xev->xclient.data.l[ 3 ];
+}
+#endif  // QT_NO_XSYNC
+
+
 /*!
     \property QWidget::acceptDrops
     \brief whether drop events are enabled for this widget
--- src/kernel/qapplication_x11.cpp.sav	2007-04-16 13:47:26.000000000 +0200
+++ src/kernel/qapplication_x11.cpp	2007-06-23 19:49:15.000000000 +0200
@@ -285,6 +285,11 @@ Atom		*qt_net_supported_list	= 0;
 Window		*qt_net_virtual_root_list	= 0;
 
 
+// X11 SYNC support
+#ifndef QT_NO_XSYNC
+Atom		qt_net_wm_sync_request_counter	= 0;
+Atom		qt_net_wm_sync_request     	= 0;
+#endif
 
 // client leader window
 Window qt_x11_wm_client_leader = 0;
@@ -309,6 +314,13 @@ static int xrandr_eventbase;
 // Display
 Q_EXPORT bool qt_use_xrender = FALSE;
 
+#ifndef QT_NO_XSYNC
+// True if SYNC extension exists on the connected display
+bool qt_use_xsync = FALSE;
+static int xsync_eventbase;
+static int xsync_errorbase;
+#endif
+
 // modifier masks for alt/meta - detected when the application starts
 static long qt_alt_mask = 0;
 static long qt_meta_mask = 0;
@@ -1938,6 +1950,11 @@ void qt_init_internal( int *argcptr, cha
 	qt_x11_intern_atom( "UTF8_STRING", &qt_utf8_string );
         qt_x11_intern_atom( "_SGI_DESKS_MANAGER", &qt_sgi_desks_manager );
 
+#ifndef QT_NO_XSYNC
+	qt_x11_intern_atom( "_NET_WM_SYNC_REQUEST_COUNTER", &qt_net_wm_sync_request_counter );
+	qt_x11_intern_atom( "_NET_WM_SYNC_REQUEST", &qt_net_wm_sync_request );
+#endif
+
 	qt_xdnd_setup();
 	qt_x11_motifdnd_init();
 
@@ -1974,6 +1991,15 @@ void qt_init_internal( int *argcptr, cha
 	}
 #endif // QT_NO_XRENDER
 
+#ifndef QT_NO_XSYNC
+	// Try to initialize SYNC extension on the connected display
+	int xsync_major, xsync_minor;
+	if ( XSyncQueryExtension( appDpy, &xsync_eventbase, &xsync_errorbase ) && 
+	     XSyncInitialize( appDpy, &xsync_major,  &xsync_minor ) ) {
+	     qt_use_xsync = TRUE;
+	}
+#endif 
+
 #ifndef QT_NO_XKB
 	// If XKB is detected, set the GrabsUseXKBState option so input method
 	// compositions continue to work (ie. deadkeys)
@@ -3141,6 +3167,10 @@ int QApplication::x11ClientMessage(QWidg
 		    XSendEvent( event->xclient.display, event->xclient.window,
 				False, SubstructureNotifyMask|SubstructureRedirectMask, event );
 		}
+#ifndef QT_NO_XSYNC
+	    } else if (a == qt_net_wm_sync_request ) {
+		    widget->handleSyncRequest( event );
+#endif
 	    }
 	} else if ( event->xclient.message_type == qt_qt_scrolldone ) {
 	    widget->translateScrollDoneEvent(event);
@@ -5681,6 +5711,21 @@ bool QETWidget::translateScrollDoneEvent
     return FALSE;
 }
 
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+#ifndef QT_NO_XSYNC
+static Bool qt_net_wm_sync_request_scanner(Display*, XEvent* event, XPointer arg)
+{
+    return (event->type == ClientMessage && event->xclient.window == *(Window*)arg
+        && event->xclient.message_type == qt_wm_protocols
+        && event->xclient.data.l[ 0 ] == qt_net_wm_sync_request );
+}
+#endif
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
 
 //
 // ConfigureNotify (window move and resize) event translation
@@ -5712,6 +5757,7 @@ bool QETWidget::translateConfigEvent( co
         if (! extra || extra->compress_events) {
             // ConfigureNotify compression for faster opaque resizing
             XEvent otherEvent;
+            int compressed_configs = 0;
             while ( XCheckTypedWindowEvent( x11Display(), winId(), ConfigureNotify,
                                             &otherEvent ) ) {
                 if ( qt_x11EventFilter( &otherEvent ) )
@@ -5732,7 +5778,18 @@ bool QETWidget::translateConfigEvent( co
                     newCPos.ry() = otherEvent.xconfigure.y +
                                    otherEvent.xconfigure.border_width;
                 }
+                ++compressed_configs;
+            }
+#ifndef QT_NO_XSYNC
+            // _NET_WM_SYNC_REQUEST compression 
+            Window wid = winId();
+            while ( compressed_configs &&
+                    XCheckIfEvent( x11Display(), &otherEvent,
+                    qt_net_wm_sync_request_scanner, (XPointer)&wid ) ) {
+                handleSyncRequest( (void*)&otherEvent );
+                --compressed_configs;
             }
+#endif
         }
 
 	QRect cr ( geometry() );
@@ -5786,6 +5843,8 @@ bool QETWidget::translateConfigEvent( co
 	repaint( !testWFlags(WResizeNoErase) || transbg );
     }
 
+    incrementSyncCounter();
+
     return TRUE;
 }