Add an error signal to QQuickWindow
authorLaszlo Agocs <laszlo.agocs@digia.com>
Thu, 20 Feb 2014 15:14:45 +0000 (16:14 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Fri, 21 Feb 2014 15:29:07 +0000 (16:29 +0100)
When nothing is connected to this signal, an error will be printed or,
in case of Windows, a message box will be shown. If there is something
connected, it is up to the application to handle the error.

[ChangeLog] Added a new sceneGraphError() signal to QQuickWindow
which applications can use to detect errors like OpenGL context creation
failures and react in their own custom ways.

Task-number: QTBUG-36138
Change-Id: I33b1e5e0e3f25872af67c5bb5ae937e3470b25f3
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
src/quick/items/qquickwindow.cpp
src/quick/items/qquickwindow.h
src/quick/items/qquickwindow_p.h
src/quick/quick.pro
src/quick/scenegraph/qsgrenderloop.cpp
src/quick/scenegraph/qsgthreadedrenderloop.cpp
src/quick/scenegraph/qsgwindowsrenderloop.cpp

index 588120a..014fb51 100644 (file)
@@ -2675,6 +2675,17 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
 
 }
 
+bool QQuickWindowPrivate::emitError(QQuickWindow::SceneGraphError error, const QString &msg)
+{
+    Q_Q(QQuickWindow);
+    static const QMetaMethod errorSignal = QMetaMethod::fromSignal(&QQuickWindow::sceneGraphError);
+    if (q->isSignalConnected(errorSignal)) {
+        emit q->sceneGraphError(error, msg);
+        return true;
+    }
+    return false;
+}
+
 void QQuickWindow::maybeUpdate()
 {
     Q_D(QQuickWindow);
@@ -2750,6 +2761,21 @@ QOpenGLContext *QQuickWindow::openglContext() const
  */
 
 /*!
+    \fn void QQuickWindow::sceneGraphError(SceneGraphError error, const QString &message)
+
+    This signal is emitted when an error occurred during scene graph initialization.
+
+    Applications should connect to this signal if they wish to handle errors,
+    like OpenGL context creation failures, in a custom way. When no slot is
+    connected to the signal, the behavior will be different: Quick will print
+    the message, or show a message box, and terminate the application.
+
+    This signal will be emitted from the gui thread.
+
+    \since 5.3
+ */
+
+/*!
     \class QQuickCloseEvent
     \internal
     \since 5.1
@@ -2983,6 +3009,20 @@ QQmlIncubationController *QQuickWindow::incubationController() const
  */
 
 /*!
+    \enum QQuickWindow::SceneGraphError
+
+    This enum describes the error in a sceneGraphError() signal.
+
+    \value ContextNotAvailable OpenGL context creation failed. This typically means that
+    no suitable OpenGL implementation was found, for example because no graphics drivers
+    are installed and so no OpenGL 2 support is present. On mobile and embedded boards
+    that use OpenGL ES such an error is likely to indicate issues in the windowing system
+    integration and possibly an incorrect configuration of Qt.
+
+    \since 5.3
+ */
+
+/*!
     \fn void QQuickWindow::beforeSynchronizing()
 
     This signal is emitted before the scene graph is synchronized with the QML state.
index ced2324..8be6cc8 100644 (file)
@@ -79,6 +79,11 @@ public:
 
     Q_DECLARE_FLAGS(CreateTextureOptions, CreateTextureOption)
 
+    enum SceneGraphError {
+        ContextNotAvailable = 1
+    };
+    Q_ENUMS(SceneGraphError)
+
     QQuickWindow(QWindow *parent = 0);
 
     virtual ~QQuickWindow();
@@ -142,6 +147,7 @@ Q_SIGNALS:
     Q_REVISION(1) void closing(QQuickCloseEvent *close);
     void colorChanged(const QColor &);
     Q_REVISION(1) void activeFocusItemChanged();
+    void sceneGraphError(QQuickWindow::SceneGraphError error, const QString &message);
 
 public Q_SLOTS:
     void update();
index 5f61403..4fb3d0f 100644 (file)
@@ -184,6 +184,8 @@ public:
 
     bool isRenderable() const;
 
+    bool emitError(QQuickWindow::SceneGraphError error, const QString &msg);
+
     QQuickItem::UpdatePaintNodeData updatePaintNodeData;
 
     QQuickItem *dirtyItemList;
index e1fccbe..38e743c 100644 (file)
@@ -6,6 +6,7 @@ QT_PRIVATE =  network
 DEFINES   += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES
 win32-msvc*:DEFINES *= _CRT_SECURE_NO_WARNINGS
 solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2
+win32:!wince:!winrt: LIBS += -luser32
 
 exists("qqml_enable_gcov") {
     QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage -fno-elide-constructors
index 50c5b14..08ab380 100644 (file)
@@ -281,9 +281,18 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
         if (QSGContext::sharedOpenGLContext())
             gl->setShareContext(QSGContext::sharedOpenGLContext());
         if (!gl->create()) {
-            qWarning("QtQuick: failed to create OpenGL context");
             delete gl;
             gl = 0;
+            QString formatStr;
+            QDebug(&formatStr) << window->requestedFormat();
+            QString contextType = QLatin1String(QOpenGLFunctions::isES() ? "EGL" : "OpenGL");
+            const char *msg = QT_TRANSLATE_NOOP("QSGGuiThreadRenderLoop", "Failed to create %1 context for format %2");
+            QString translatedMsg = tr(msg).arg(contextType).arg(formatStr);
+            QString nonTranslatedMsg = QString(QLatin1String(msg)).arg(contextType).arg(formatStr);
+            bool signalEmitted = QQuickWindowPrivate::get(window)->emitError(QQuickWindow::ContextNotAvailable,
+                                                                             translatedMsg);
+            if (!signalEmitted)
+                qFatal("%s", qPrintable(nonTranslatedMsg));
         } else {
             current = gl->makeCurrent(window);
         }
index ddf724b..7b5f161 100644 (file)
@@ -914,7 +914,17 @@ void QSGThreadedRenderLoop::handleExposure(Window *w)
             if (!w->thread->gl->create()) {
                 delete w->thread->gl;
                 w->thread->gl = 0;
-                qWarning("QtQuick: failed to create OpenGL context");
+                QString formatStr;
+                QDebug(&formatStr) << w->window->requestedFormat();
+                QString contextType = QLatin1String(QOpenGLFunctions::isES() ? "EGL" : "OpenGL");
+                const char *msg = QT_TRANSLATE_NOOP("QSGThreadedRenderLoop",
+                                                    "Failed to create %1 context for format %2");
+                QString translatedMsg = tr(msg).arg(contextType).arg(formatStr);
+                QString nonTranslatedMsg = QString(QLatin1String(msg)).arg(contextType).arg(formatStr);
+                bool signalEmitted = QQuickWindowPrivate::get(w->window)->emitError(QQuickWindow::ContextNotAvailable,
+                                                                                    translatedMsg);
+                if (!signalEmitted)
+                    qFatal("%s", qPrintable(nonTranslatedMsg));
                 return;
             }
 
index 204a303..8af0401 100644 (file)
@@ -42,6 +42,7 @@
 #include "qsgwindowsrenderloop_p.h"
 
 #include <QtCore/QCoreApplication>
+#include <QtCore/QLibraryInfo>
 
 #include <QtGui/QScreen>
 #include <QtGui/QGuiApplication>
@@ -182,9 +183,35 @@ void QSGWindowsRenderLoop::show(QQuickWindow *window)
             m_gl->setShareContext(QSGContext::sharedOpenGLContext());
         bool created = m_gl->create();
         if (!created) {
-            qWarning("QtQuick: failed to create OpenGL context");
+            const bool isDebug = QLibraryInfo::isDebugBuild();
+            QString eglLibName = QLatin1String(isDebug ? "libEGLd.dll" : "libEGL.dll");
+            QString glesLibName = QLatin1String(isDebug ? "libGLESv2d.dll" : "libGLESv2.dll");
+            QString contextType = QLatin1String(QOpenGLFunctions::isES() ? "EGL" : "OpenGL");
+            const char *msg = QT_TRANSLATE_NOOP(
+                "QSGWindowsRenderLoop",
+                "Failed to create %1 context. "
+                "This is most likely caused by not having the necessary graphics drivers installed.\n\n"
+                "Install a driver providing OpenGL 2.0 or higher, or, if this is not possible, "
+                "make sure the Angle Open GL ES 2.0 emulation libraries (%2, %3 and d3dcompiler_*.dll) "
+                "are available in the application executable's directory or in a location listed in PATH.");
+            QString translatedMsg = tr(msg).arg(contextType).arg(eglLibName).arg(glesLibName);
+            QString nonTranslatedMsg = QString(QLatin1String(msg)).arg(contextType).arg(eglLibName).arg(glesLibName);
+            // If there is a slot connected to the error signal, emit it and leave it to
+            // the application to do something with the message. If nothing is connected,
+            // show a message on our own and terminate.
+            bool signalEmitted = QQuickWindowPrivate::get(window)->emitError(QQuickWindow::ContextNotAvailable,
+                                                                             translatedMsg);
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
+            if (!signalEmitted && !isDebug && !GetConsoleWindow()) {
+                MessageBox(0, (LPCTSTR) translatedMsg.utf16(),
+                           (LPCTSTR)(QCoreApplication::applicationName().utf16()),
+                           MB_OK | MB_ICONERROR);
+            }
+#endif // !Q_OS_WINCE && !Q_OS_WINRT
             delete m_gl;
             m_gl = 0;
+            if (!signalEmitted)
+                qFatal("%s", qPrintable(nonTranslatedMsg));
             return;
         }
         QSG_RENDER_TIMING_SAMPLE(time_created);