Added QScreen::refreshRate() to get the vertical refresh rate.
authorSamuel Rødal <samuel.rodal@nokia.com>
Fri, 4 May 2012 11:10:41 +0000 (13:10 +0200)
committerQt by Nokia <qt-info@nokia.com>
Tue, 8 May 2012 20:30:10 +0000 (22:30 +0200)
To give applications that want it the option to use a fixed timestep for
animations, and to avoid having values of 60 hard-coded (we have a
couple of those in qtdeclarative/src/quick already), we need to know the
refresh rates of the screens we are rendering to.

Change-Id: Ife49162e830440ad7eab563a27e8aebbbafc5fc5
Reviewed-by: Girish Ramakrishnan <girish.1.ramakrishnan@nokia.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
20 files changed:
config.tests/qpa/xcb/xcb.cpp
config.tests/qpa/xcb/xcb.pro
src/gui/kernel/qguiapplication.cpp
src/gui/kernel/qguiapplication_p.h
src/gui/kernel/qplatformscreen.h
src/gui/kernel/qplatformscreen_qpa.cpp
src/gui/kernel/qscreen.cpp
src/gui/kernel/qscreen.h
src/gui/kernel/qscreen_p.h
src/gui/kernel/qwindowsysteminterface_qpa.cpp
src/gui/kernel/qwindowsysteminterface_qpa.h
src/gui/kernel/qwindowsysteminterface_qpa_p.h
src/plugins/platforms/windows/qwindowsscreen.cpp
src/plugins/platforms/windows/qwindowsscreen.h
src/plugins/platforms/xcb/README
src/plugins/platforms/xcb/qxcbconnection.cpp
src/plugins/platforms/xcb/qxcbconnection.h
src/plugins/platforms/xcb/qxcbscreen.cpp
src/plugins/platforms/xcb/qxcbscreen.h
src/plugins/platforms/xcb/xcb.pro

index 6037f15..0ffeab1 100644 (file)
@@ -50,6 +50,7 @@
 #include <xcb/xcb_image.h>
 #include <xcb/xcb_keysyms.h>
 #include <xcb/sync.h>
+#include <xcb/randr.h>
 #include <xcb/shm.h>
 
 int main(int, char **)
index ed68bca..23eedb0 100644 (file)
@@ -1,5 +1,5 @@
 SOURCES = xcb.cpp
 CONFIG -= qt
 
-LIBS += -lxcb -lxcb-image -lxcb-keysyms -lxcb-icccm -lxcb-sync -lxcb-xfixes
+LIBS += -lxcb -lxcb-image -lxcb-keysyms -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-randr
 
index 631592a..62303ee 100644 (file)
@@ -1090,6 +1090,10 @@ void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePriv
         QGuiApplicationPrivate::reportLogicalDotsPerInchChange(
                 static_cast<QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInchEvent *>(e));
         break;
+    case QWindowSystemInterfacePrivate::ScreenRefreshRate:
+        QGuiApplicationPrivate::reportRefreshRateChange(
+                static_cast<QWindowSystemInterfacePrivate::ScreenRefreshRateEvent *>(e));
+        break;
     case QWindowSystemInterfacePrivate::ThemeChange:
         QGuiApplicationPrivate::processThemeChanged(
                     static_cast<QWindowSystemInterfacePrivate::ThemeChangeEvent *>(e));
@@ -1750,6 +1754,21 @@ void QGuiApplicationPrivate::reportLogicalDotsPerInchChange(QWindowSystemInterfa
     emit s->logicalDotsPerInchChanged(s->logicalDotsPerInch());
 }
 
+void QGuiApplicationPrivate::reportRefreshRateChange(QWindowSystemInterfacePrivate::ScreenRefreshRateEvent *e)
+{
+    // This operation only makes sense after the QGuiApplication constructor runs
+    if (QCoreApplication::startingUp())
+        return;
+
+    if (!e->screen)
+        return;
+
+    QScreen *s = e->screen.data();
+    s->d_func()->refreshRate = e->rate;
+
+    emit s->refreshRateChanged(s->refreshRate());
+}
+
 void QGuiApplicationPrivate::processExposeEvent(QWindowSystemInterfacePrivate::ExposeEvent *e)
 {
     if (!e->exposed)
index cb3587b..860c41d 100644 (file)
@@ -117,6 +117,7 @@ public:
     static void reportGeometryChange(QWindowSystemInterfacePrivate::ScreenGeometryEvent *e);
     static void reportAvailableGeometryChange(QWindowSystemInterfacePrivate::ScreenAvailableGeometryEvent *e);
     static void reportLogicalDotsPerInchChange(QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInchEvent *e);
+    static void reportRefreshRateChange(QWindowSystemInterfacePrivate::ScreenRefreshRateEvent *e);
     static void processThemeChanged(QWindowSystemInterfacePrivate::ThemeChangeEvent *tce);
 
     static void processExposeEvent(QWindowSystemInterfacePrivate::ExposeEvent *e);
index 075dfb1..1743024 100644 (file)
@@ -101,6 +101,8 @@ public:
     virtual QSizeF physicalSize() const;
     virtual QDpi logicalDpi() const;
 
+    virtual qreal refreshRate() const;
+
     virtual Qt::ScreenOrientation orientation() const;
 
     virtual QWindow *topLevelAt(const QPoint &point) const;
index 6c2f98b..1fd96a8 100644 (file)
@@ -161,6 +161,17 @@ QDpi QPlatformScreen::logicalDpi() const
 }
 
 /*!
+    Reimplement this function in subclass to return the vertical refresh rate
+    of the screen, in Hz.
+
+    The default returns 60, a sensible default for modern displays.
+*/
+qreal QPlatformScreen::refreshRate() const
+{
+    return 60;
+}
+
+/*!
     Reimplement this function in subclass to return the current orientation
     of the screen, for example based on accelerometer data to determine
     the device orientation.
index 716caac..2e0df43 100644 (file)
@@ -364,6 +364,16 @@ Qt::ScreenOrientation QScreen::orientation() const
 }
 
 /*!
+  \property QScreen::refreshRate
+  \brief the approximate vertical refresh rate of the screen in Hz
+*/
+qreal QScreen::refreshRate() const
+{
+    Q_D(const QScreen);
+    return d->refreshRate;
+}
+
+/*!
     \property QScreen::primaryOrientation
     \brief the primary screen orientation
 
index f69e04a..17f3cd3 100644 (file)
@@ -83,6 +83,7 @@ class Q_GUI_EXPORT QScreen : public QObject
     Q_PROPERTY(QRect availableGeometry READ availableGeometry NOTIFY availableGeometryChanged)
     Q_PROPERTY(Qt::ScreenOrientation primaryOrientation READ orientation NOTIFY primaryOrientationChanged)
     Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation NOTIFY orientationChanged)
+    Q_PROPERTY(qreal refreshRate READ refreshRate NOTIFY refreshRateChanged)
 
 public:
     QPlatformScreen *handle() const;
@@ -127,6 +128,8 @@ public:
 
     QPixmap grabWindow(WId window, int x, int y, int w, int h);
 
+    qreal refreshRate() const;
+
 Q_SIGNALS:
     void sizeChanged(const QSize &size);
     void geometryChanged(const QRect &geometry);
@@ -140,6 +143,7 @@ Q_SIGNALS:
     void availableGeometryChanged(const QRect &rect);
     void primaryOrientationChanged(Qt::ScreenOrientation orientation);
     void orientationChanged(Qt::ScreenOrientation orientation);
+    void refreshRateChanged(qreal refreshRate);
 
 private:
     explicit QScreen(QPlatformScreen *screen);
index 0167384..b0e1b86 100644 (file)
@@ -63,6 +63,7 @@ public:
         geometry = screen->geometry();
         availableGeometry = screen->availableGeometry();
         logicalDpi = screen->logicalDpi();
+        refreshRate = screen->refreshRate();
 
         updatePrimaryOrientation();
     }
@@ -74,6 +75,7 @@ public:
     QRect geometry;
     QRect availableGeometry;
     QDpi logicalDpi;
+    qreal refreshRate;
 
     QPlatformScreen *platformScreen;
 };
index 83cf8c3..c3136b0 100644 (file)
@@ -434,6 +434,13 @@ void QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(QScreen *scree
     QWindowSystemInterfacePrivate::queueWindowSystemEvent(e);
 }
 
+void QWindowSystemInterface::handleScreenRefreshRateChange(QScreen *screen, qreal newRefreshRate)
+{
+    QWindowSystemInterfacePrivate::ScreenRefreshRateEvent *e =
+            new QWindowSystemInterfacePrivate::ScreenRefreshRateEvent(screen, newRefreshRate);
+    QWindowSystemInterfacePrivate::queueWindowSystemEvent(e);
+}
+
 void QWindowSystemInterface::handleThemeChange(QWindow *tlw)
 {
     QWindowSystemInterfacePrivate::ThemeChangeEvent *e = new QWindowSystemInterfacePrivate::ThemeChangeEvent(tlw);
index 1fbf430..7e0ebf0 100644 (file)
@@ -144,6 +144,7 @@ public:
     static void handleScreenGeometryChange(QScreen *screen, const QRect &newGeometry);
     static void handleScreenAvailableGeometryChange(QScreen *screen, const QRect &newAvailableGeometry);
     static void handleScreenLogicalDotsPerInchChange(QScreen *screen, qreal newDpiX, qreal newDpiY);
+    static void handleScreenRefreshRateChange(QScreen *screen, qreal newRefreshRate);
 
     static void handleThemeChange(QWindow *tlw);
 
index d7be769..c7ad197 100644 (file)
@@ -66,6 +66,7 @@ public:
         ScreenGeometry,
         ScreenAvailableGeometry,
         ScreenLogicalDotsPerInch,
+        ScreenRefreshRate,
         ThemeChange,
         Expose
     };
@@ -233,6 +234,14 @@ public:
         qreal dpiY;
     };
 
+    class ScreenRefreshRateEvent : public WindowSystemEvent {
+    public:
+        ScreenRefreshRateEvent(QScreen *s, qreal r)
+            : WindowSystemEvent(ScreenRefreshRate), screen(s), rate(r) { }
+        QWeakPointer<QScreen> screen;
+        qreal rate;
+    };
+
     class ThemeChangeEvent : public WindowSystemEvent {
     public:
         explicit ThemeChangeEvent(QWindow * w)
index 2476e15..cc694fe 100644 (file)
@@ -59,7 +59,8 @@ QT_BEGIN_NAMESPACE
 
 QWindowsScreenData::QWindowsScreenData() :
     dpi(96, 96), depth(32), format(QImage::Format_ARGB32_Premultiplied),
-    flags(VirtualDesktop), orientation(Qt::LandscapeOrientation)
+    flags(VirtualDesktop), orientation(Qt::LandscapeOrientation),
+    refreshRateHz(60)
 {
 }
 
@@ -103,6 +104,9 @@ BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM
         data.depth = GetDeviceCaps(hdc, BITSPIXEL);
         data.format = data.depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32;
         data.physicalSizeMM = QSizeF(GetDeviceCaps(hdc, HORZSIZE), GetDeviceCaps(hdc, VERTSIZE));
+        const int refreshRate = GetDeviceCaps(hdc, VREFRESH);
+        if (refreshRate > 1) // 0,1 means heardware default.
+            data.refreshRateHz = refreshRate;
         DeleteDC(hdc);
     } else {
         qWarning("%s: Unable to obtain handle for monitor '%s', defaulting to %g DPI.",
index 56e9ab3..615d4fa 100644 (file)
@@ -70,6 +70,7 @@ struct QWindowsScreenData
     unsigned flags;
     QString name;
     Qt::ScreenOrientation orientation;
+    qreal refreshRateHz;
 };
 
 class QWindowsScreen : public QPlatformScreen
@@ -87,6 +88,7 @@ public:
     virtual QImage::Format format() const { return m_data.format; }
     virtual QSizeF physicalSize() const { return m_data.physicalSizeMM; }
     virtual QDpi logicalDpi() const { return m_data.dpi; }
+    virtual qreal refreshRate() const { return m_data.refreshRateHz; }
     virtual QString name() const { return m_data.name; }
     virtual Qt::ScreenOrientation primaryOrientation() { return m_data.orientation; }
     virtual QList<QPlatformScreen *> virtualSiblings() const;
index eede0cc..14e98b8 100644 (file)
@@ -1,8 +1,8 @@
 Requires libxcb >= 1.5.
 
 Required packages:
-libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm1 libxcb-icccm1-dev libxcb-sync0 libxcb-sync0-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev
+libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm1 libxcb-icccm1-dev libxcb-sync0 libxcb-sync0-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev
 
 On Ubuntu 11.10 icccm1 is replaced by icccm4 and xcb-render-util is not available:
-libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-sync0 libxcb-sync0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev
+libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-sync0 libxcb-sync0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev
 The packages for xcb-render-util can be installed manually from http://packages.ubuntu.com/natty/libxcb-render-util0 and http://packages.ubuntu.com/natty/libxcb-render-util0-dev
index 76979bf..728dd1d 100644 (file)
@@ -60,6 +60,7 @@
 #include <stdio.h>
 #include <errno.h>
 #include <xcb/xfixes.h>
+#include <xcb/randr.h>
 
 #ifdef XCB_USE_XLIB
 #include <X11/Xlib.h>
@@ -109,7 +110,9 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, const char
     , m_has_support_for_dri2(false)
 #endif
     , xfixes_first_event(0)
+    , xrandr_first_event(0)
     , has_shape_extension(false)
+    , has_randr_extension(false)
     , has_input_shape(false)
 {
     m_primaryScreen = 0;
@@ -160,6 +163,8 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, const char
 
     xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup);
 
+    initializeXRandr();
+
     int screenNumber = 0;
     while (it.rem) {
         m_screens << new QXcbScreen(this, it.data, screenNumber++);
@@ -586,6 +591,15 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
             setTime(((xcb_xfixes_selection_notify_event_t *)event)->timestamp);
             m_clipboard->handleXFixesSelectionRequest((xcb_xfixes_selection_notify_event_t *)event);
             handled = true;
+        } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
+            xcb_randr_screen_change_notify_event_t *change_event = (xcb_randr_screen_change_notify_event_t *)event;
+            foreach (QXcbScreen *s, m_screens) {
+                if (s->root() == change_event->root ) {
+                    s->updateRefreshRate();
+                    break;
+                }
+            }
+            handled = true;
         }
     }
 
@@ -1060,6 +1074,31 @@ void QXcbConnection::initializeXRender()
 #endif
 }
 
+void QXcbConnection::initializeXRandr()
+{
+    const xcb_query_extension_reply_t *xrandr_reply = xcb_get_extension_data(m_connection, &xcb_randr_id);
+    if (!xrandr_reply || !xrandr_reply->present)
+        return;
+
+    xrandr_first_event = xrandr_reply->first_event;
+
+    xcb_generic_error_t *error = 0;
+    xcb_randr_query_version_cookie_t xrandr_query_cookie = xcb_randr_query_version(m_connection,
+                                                                                   XCB_RANDR_MAJOR_VERSION,
+                                                                                   XCB_RANDR_MINOR_VERSION);
+
+    has_randr_extension = true;
+
+    xcb_randr_query_version_reply_t *xrandr_query = xcb_randr_query_version_reply(m_connection,
+                                                                                  xrandr_query_cookie, &error);
+    if (!xrandr_query || error || (xrandr_query->major_version < 1 || (xrandr_query->major_version == 1 && xrandr_query->minor_version < 2))) {
+        qWarning("QXcbConnection: Failed to initialize XRandr");
+        free(error);
+        has_randr_extension = false;
+    }
+    free(xrandr_query);
+}
+
 void QXcbConnection::initializeXShape()
 {
     const xcb_query_extension_reply_t *xshape_reply = xcb_get_extension_data(m_connection, &xcb_shape_id);
index 34943bf..86eaf5d 100644 (file)
@@ -355,6 +355,7 @@ public:
 
     bool hasXFixes() const { return xfixes_first_event > 0; }
     bool hasXShape() const { return has_shape_extension; }
+    bool hasXRandr() const { return has_randr_extension; }
     bool hasInputShape() const { return has_input_shape; }
 
 private slots:
@@ -365,6 +366,7 @@ private:
     void sendConnectionEvent(QXcbAtom::Atom atom, uint id = 0);
     void initializeXFixes();
     void initializeXRender();
+    void initializeXRandr();
     void initializeXShape();
 #ifdef XCB_USE_DRI2
     void initializeDri2();
@@ -432,8 +434,10 @@ private:
     QVector<PeekFunc> m_peekFuncs;
 
     uint32_t xfixes_first_event;
+    uint32_t xrandr_first_event;
 
     bool has_shape_extension;
+    bool has_randr_extension;
     bool has_input_shape;
 };
 
index ae5e4cc..fbfaa89 100644 (file)
 
 #include <QDebug>
 
+#include <xcb/randr.h>
+
+#include <QtGui/QWindowSystemInterface>
+
 QT_BEGIN_NAMESPACE
 
 QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *screen, int number)
     : QXcbObject(connection)
     , m_screen(screen)
     , m_number(number)
+    , m_refreshRate(60)
 {
+    if (connection->hasXRandr())
+        xcb_randr_select_input(xcb_connection(), screen->root, true);
+
+    updateRefreshRate();
+
 #ifdef Q_XCB_DEBUG
     qDebug();
     qDebug("Information of screen %d:", screen->root);
@@ -63,6 +73,7 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *screen, int num
     qDebug("  depth.........: %d", screen->root_depth);
     qDebug("  white pixel...: %x", screen->white_pixel);
     qDebug("  black pixel...: %x", screen->black_pixel);
+    qDebug("  refresh rate...: %d", m_refreshRate);
     qDebug();
 #endif
 
@@ -228,6 +239,34 @@ QPlatformCursor *QXcbScreen::cursor() const
     return m_cursor;
 }
 
+qreal QXcbScreen::refreshRate() const
+{
+    return m_refreshRate;
+}
+
+void QXcbScreen::updateRefreshRate()
+{
+    if (!connection()->hasXRandr())
+        return;
+
+    int rate = m_refreshRate;
+
+    xcb_randr_get_screen_info_reply_t *screenInfoReply =
+        xcb_randr_get_screen_info_reply(xcb_connection(), xcb_randr_get_screen_info_unchecked(xcb_connection(), m_screen->root), 0);
+
+    if (screenInfoReply) {
+        rate = screenInfoReply->rate;
+        free(screenInfoReply);
+    }
+
+    if (rate == m_refreshRate)
+        return;
+
+    m_refreshRate = rate;
+
+    QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), rate);
+}
+
 int QXcbScreen::screenNumber() const
 {
     return m_number;
index ba3f4af..7a81d8b 100644 (file)
@@ -69,6 +69,7 @@ public:
     QImage::Format format() const;
     QSizeF physicalSize() const;
     QPlatformCursor *cursor() const;
+    qreal refreshRate() const;
 
     int screenNumber() const;
 
@@ -84,6 +85,8 @@ public:
 
     QString name() const;
 
+    void updateRefreshRate();
+
 private:
     xcb_screen_t *m_screen;
     int m_number;
@@ -92,6 +95,7 @@ private:
     xcb_window_t m_clientLeader;
     QMap<xcb_visualid_t, xcb_visualtype_t> m_visuals;
     QXcbCursor *m_cursor;
+    int m_refreshRate;
 };
 
 QT_END_NAMESPACE
index 0da5fb1..210b770 100644 (file)
@@ -91,7 +91,7 @@ contains(DEFINES, XCB_USE_DRI2) {
     }
 }
 
-LIBS += -lxcb -lxcb-image -lxcb-keysyms -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shape -lxcb-shm
+LIBS += -lxcb -lxcb-image -lxcb-keysyms -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shape -lxcb-shm -lxcb-randr
 
 DEFINES += $$QMAKE_DEFINES_XCB
 LIBS += $$QMAKE_LIBS_XCB