src/hbcore/gui/hbsplashscreen.cpp
changeset 34 ed14f46c0e55
parent 5 627c4a0fd0e7
equal deleted inserted replaced
31:7516d6d86cf5 34:ed14f46c0e55
    24 ****************************************************************************/
    24 ****************************************************************************/
    25 
    25 
    26 #include "hbsplashscreen.h"
    26 #include "hbsplashscreen.h"
    27 #include "hbsplashscreen_generic_p.h"
    27 #include "hbsplashscreen_generic_p.h"
    28 #include "hbsplash_p.h"
    28 #include "hbsplash_p.h"
       
    29 #include "hbsplashdefs_p.h"
       
    30 #include "hbapplication.h"
    29 #include <QPainter>
    31 #include <QPainter>
    30 #include <QApplication>
    32 #include <QApplication>
       
    33 
       
    34 #if QT_VERSION >= 0x040700
       
    35 #include <QElapsedTimer>
       
    36 #define ELAPSED_TIMER QElapsedTimer
       
    37 #else
       
    38 #include <QTime>
       
    39 #define ELAPSED_TIMER QTime
       
    40 #endif
       
    41 
       
    42 #if defined(Q_OS_SYMBIAN) && defined(HB_EFFECTS_OPENVG)
       
    43 // When Symbian/EGL/OpenVG is available we can use the more efficient
       
    44 // implementation which is not only faster but is able to operate
       
    45 // without having Qt or any other framework initialized. This means
       
    46 // having truly 'instant' splash screens.
       
    47 #define HB_SPLASH_DIRECT_WS
       
    48 #endif
       
    49 
       
    50 #ifdef HB_SPLASH_DIRECT_WS
       
    51 #include "hbsplashscreen_symbian_vg_p.h"
       
    52 #endif
    31 
    53 
    32 /*!
    54 /*!
    33   @stable
    55   @stable
    34   @hbcore
    56   @hbcore
    35   \class HbSplashScreen
    57   \class HbSplashScreen
    59 /*!
    81 /*!
    60   \var HbSplashScreen::Flag HbSplashScreen::FixedVertical
    82   \var HbSplashScreen::Flag HbSplashScreen::FixedVertical
    61 
    83 
    62   Indicates that the application will force its orientation to vertical. As a
    84   Indicates that the application will force its orientation to vertical. As a
    63   result the splash screen will also be forced to vertical orientation.
    85   result the splash screen will also be forced to vertical orientation.
       
    86 
       
    87   \sa Hb::SplashFixedVertical
    64 */
    88 */
    65 
    89 
    66 /*!
    90 /*!
    67   \var HbSplashScreen::Flag HbSplashScreen::FixedHorizontal
    91   \var HbSplashScreen::Flag HbSplashScreen::FixedHorizontal
    68 
    92 
    69   Indicates that the application will force its orientation to horizontal. As a
    93   Indicates that the application will force its orientation to horizontal. As a
    70   result the splash screen will also be forced to horizontal orientation.
    94   result the splash screen will also be forced to horizontal orientation.
       
    95 
       
    96   \sa Hb::SplashFixedHorizontal
       
    97 */
       
    98 
       
    99 /*!
       
   100   \var HbSplashScreen::Flag HbSplashScreen::ForceQt
       
   101 
       
   102   Forces the usage of the QWidget-based implementation even on platforms where a
       
   103   non-Qt based implementation would be available. The Qt-based version requires
       
   104   QApplication to be constructed, while non-Qt based ones do not need this and
       
   105   therefore they can be launched before instantiating QApplication or
       
   106   HbApplication, providing a better startup experience. However some special
       
   107   applications may want to stick to the Qt-based version, e.g. because they
       
   108   force the usage of the raster graphics system.
       
   109 
       
   110   \sa Hb::ForceQtSplash
    71 */
   111 */
    72 
   112 
    73 static HbSplashScreenInterface *splashScreen = 0;
   113 static HbSplashScreenInterface *splashScreen = 0;
    74 
   114 
    75 struct RequestProps {
   115 struct RequestProps {
    79     QString mScreenId;
   119     QString mScreenId;
    80 };
   120 };
    81 
   121 
    82 Q_GLOBAL_STATIC(RequestProps, requestProps)
   122 Q_GLOBAL_STATIC(RequestProps, requestProps)
    83 
   123 
       
   124 // This static function is called when the fw wants to know if the
       
   125 // splash screen can be launched before constructing QApplication.
       
   126 bool HbSplashScreenExt::needsQt()
       
   127 {
       
   128 #ifdef HB_SPLASH_DIRECT_WS
       
   129     return false;
       
   130 #else
       
   131     return true;
       
   132 #endif
       
   133 }
       
   134 
       
   135 // Called (by HbApplication) when QApplication is constructed and the splash was
       
   136 // launched before that. This gives a chance for the splash screen to do
       
   137 // activies that need an active scheduler or a QApplication instance.
       
   138 void HbSplashScreenExt::doQtPhase()
       
   139 {
       
   140     // Can also be called when the splash screen was not started or is already
       
   141     // destroyed, do nothing in such cases.
       
   142     if (splashScreen) {
       
   143         splashScreen->doQtPhase();
       
   144     }
       
   145 }
       
   146 
    84 /*!
   147 /*!
    85   Creates and shows the splash screen, if a suitable one is available for the
   148   Creates and shows the splash screen, if a suitable one is available for the
    86   current application. The splash screen is automatically destroyed by
   149   current application. The splash screen is automatically destroyed by
    87   HbMainWindow after the window has become fully visible.
   150   HbMainWindow after the window has become fully visible.
       
   151 
       
   152   The generic splash implementation is QWidget-based and thus needs to have the
       
   153   QApplication instance created before calling this function. On the other hand
       
   154   for Symbian there may be a Qt-independent implementation which means that
       
   155   this function can be called before constructing HbApplication or QApplication,
       
   156   i.e. right after entering main().
       
   157   
       
   158   Normally there is no need to worry about the differences because HbApplication
       
   159   is aware of this and will call this function at the most ideal time, depending
       
   160   on the underlying implementation. However when start() is called directly by
       
   161   the application, this becomes important because the application startup
       
   162   experience can be greatly improved by making sure the splash is shown as early
       
   163   as possible.
       
   164 
       
   165   On Symbian the splash will be automatically suppressed (i.e. not shown) if the
       
   166   application was started to background, that is,
       
   167   HbApplication::startedToBackground() returns true. To override this default
       
   168   behavior, pass HbSplashScreen::ShowWhenStartingToBackground.
       
   169 
       
   170   Passing argc and argv is optional, however if they are omitted,
       
   171   QCoreApplication must have already been instantiated. If neither argc and argv
       
   172   are specified nor QCoreApplication is available, the automatic splash
       
   173   suppression will not be available.
    88  */
   174  */
    89 void HbSplashScreen::start(Flags flags)
   175 void HbSplashScreen::start(Flags flags, int argc, char *argv[])
    90 {
   176 {
       
   177     Flags realFlags = flags | requestProps()->mSplashFlags;
       
   178     if (!realFlags.testFlag(ShowWhenStartingToBackground)
       
   179         && HbApplication::startedToBackground(argc, argv)) {
       
   180         splDebug("[hbsplash] app started to background, suppressing splash");
       
   181         return;
       
   182     }
    91     if (!splashScreen) {
   183     if (!splashScreen) {
       
   184 #ifdef HB_SPLASH_DIRECT_WS
       
   185         // Use the Qt-based impl even on Symbian when it is forced or when there
       
   186         // is already a QApplication instance.
       
   187         if (realFlags.testFlag(ForceQt) || QApplication::instance()) {
       
   188             splashScreen = new HbSplashScreenGeneric;
       
   189         } else {
       
   190             // Ideal case: Use the native splash.
       
   191             splashScreen = new HbSplashScreenSymbianVg;
       
   192         }
       
   193 #else
    92         splashScreen = new HbSplashScreenGeneric;
   194         splashScreen = new HbSplashScreenGeneric;
    93     }
   195 #endif
    94     splashScreen->start(flags | requestProps()->mSplashFlags);
   196     }
       
   197     splashScreen->start(realFlags);
    95 }
   198 }
    96 
   199 
    97 /*!
   200 /*!
    98     Hides and destroys the splash screen.  Has no effect if the splash screen
   201     Hides and destroys the splash screen.  Has no effect if the splash screen
    99     has not been started.  This is called automatically by HbMainWindow after
   202     has not been started.  This is called automatically by HbMainWindow after
   182 }
   285 }
   183 
   286 
   184 HbSplashScreenGeneric::~HbSplashScreenGeneric()
   287 HbSplashScreenGeneric::~HbSplashScreenGeneric()
   185 {
   288 {
   186     if (mImageData) {
   289     if (mImageData) {
   187         qDebug("[hbsplash] destroying splash screen");
   290         splDebug("[hbsplash] destroying splash screen");
   188         delete mImageData;
   291         delete mImageData;
   189     }
   292     }
   190 }
   293 }
   191 
   294 
   192 void HbSplashScreenGeneric::release()
   295 void HbSplashScreenGeneric::release()
   207                 mContents = QImage(mImageData, w, h, bpl, fmt);
   310                 mContents = QImage(mImageData, w, h, bpl, fmt);
   208                 resize(mContents.size());
   311                 resize(mContents.size());
   209             }
   312             }
   210         }
   313         }
   211         if (!mContents.isNull()) {
   314         if (!mContents.isNull()) {
   212             qDebug("[hbsplash] splash screen initialized");
   315             splDebug("[hbsplash] splash screen initialized");
       
   316 
   213 #ifdef Q_OS_SYMBIAN
   317 #ifdef Q_OS_SYMBIAN
   214             showFullScreen(); // krazy:exclude=qmethods
   318             showFullScreen(); // krazy:exclude=qmethods
   215 #else
   319 #else
   216             show();
   320             show();
   217 #endif
   321 #endif
       
   322 
   218             QApplication::processEvents();
   323             QApplication::processEvents();
   219             QApplication::flush();
   324             QApplication::flush();
       
   325 
   220             // The splash screen must be destroyed automatically when
   326             // The splash screen must be destroyed automatically when
   221             // loosing foreground.
   327             // loosing foreground.
   222             if (QApplication::instance()) {
   328             if (QApplication::instance()) {
   223                 QApplication::instance()->installEventFilter(this);
   329                 QApplication::instance()->installEventFilter(this);
   224             }
   330             }
       
   331 
   225             // The splash screen must be destroyed automatically after
   332             // The splash screen must be destroyed automatically after
   226             // a certain amount of time.
   333             // a certain amount of time.
   227             mTimerId = startTimer(auto_stop_interval);
   334             mTimerId = startTimer(auto_stop_interval);
   228         }
   335         }
   229     } catch (const std::bad_alloc &) {
   336     } catch (const std::bad_alloc &) {
   244 }
   351 }
   245 
   352 
   246 void HbSplashScreenGeneric::timerEvent(QTimerEvent *event)
   353 void HbSplashScreenGeneric::timerEvent(QTimerEvent *event)
   247 {
   354 {
   248     if (event->timerId() == mTimerId) {
   355     if (event->timerId() == mTimerId) {
   249         qDebug("[hbsplash] timeout while splash screen is active");
   356         qWarning("[hbsplash] timeout while splash screen is active");
   250         deleteLater();
   357         deleteLater();
   251         splashScreen = 0;
   358         splashScreen = 0;
   252     } else {
   359     } else {
   253         QWidget::timerEvent(event);
   360         QWidget::timerEvent(event);
   254     }
   361     }
   255 }
   362 }
   256 
   363 
   257 bool HbSplashScreenGeneric::eventFilter(QObject *obj, QEvent *event)
   364 bool HbSplashScreenGeneric::eventFilter(QObject *obj, QEvent *event)
   258 {
   365 {
   259     if (event->type() == QEvent::ApplicationDeactivate) {
   366     if (event->type() == QEvent::ApplicationDeactivate) {
   260         qDebug("[hbsplash] foreground lost while splash screen is active");
   367         qWarning("[hbsplash] foreground lost while splash screen is active");
   261         deleteLater();
   368         deleteLater();
   262         splashScreen = 0;
   369         splashScreen = 0;
   263     }
   370     }
   264     return QWidget::eventFilter(obj, event);
   371     return QWidget::eventFilter(obj, event);
   265 }
   372 }
       
   373 
       
   374 // Symbian (wserv + egl + openvg) implementation.
       
   375 
       
   376 // Unlike the generic version this works also when used
       
   377 // before constructing the QApplication instance.
       
   378 
       
   379 // No mw layer frameworks (cone, uikon) must be used here
       
   380 // because they may not be initialized at all and doing
       
   381 // initialization here would interfere with Qt.
       
   382 
       
   383 #ifdef HB_SPLASH_DIRECT_WS
       
   384 
       
   385 HbSplashScreenSymbianVg::HbSplashScreenSymbianVg()
       
   386     : mInited(false), mImageData(0), mTimer(0)
       
   387 {
       
   388 }
       
   389 
       
   390 HbSplashScreenSymbianVg::~HbSplashScreenSymbianVg()
       
   391 {
       
   392     delete mImageData;
       
   393     delete mTimer;
       
   394     if (mInited) {
       
   395 #ifdef HB_SPLASH_VERBOSE_LOGGING
       
   396         RDebug::Printf("[hbsplash] destroying splash screen");
       
   397 #endif
       
   398         if (eglGetCurrentContext() == mContext
       
   399             || eglGetCurrentSurface(EGL_READ) == mSurface
       
   400             || eglGetCurrentSurface(EGL_DRAW) == mSurface)
       
   401         {
       
   402             eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
       
   403         }
       
   404         eglDestroySurface(mDisplay, mSurface);
       
   405         eglDestroyContext(mDisplay, mContext);
       
   406         delete mScr;
       
   407         mGroup.Close();
       
   408         mWs.Close();
       
   409     }
       
   410 }
       
   411 
       
   412 void HbSplashScreenSymbianVg::release()
       
   413 {
       
   414     delete this;
       
   415 }
       
   416 
       
   417 bool HbSplashScreenSymbianVg::init()
       
   418 {
       
   419     // This is typically called before initializing anything, meaning
       
   420     // there is no active scheduler, cleanup stack, cone, etc.
       
   421 
       
   422 #ifdef HB_SPLASH_VERBOSE_LOGGING
       
   423     ELAPSED_TIMER t;
       
   424     t.start();
       
   425 #endif
       
   426 
       
   427     if (mWs.Connect() != KErrNone) { // also connects to fbserv
       
   428         return false;
       
   429     }
       
   430     try {
       
   431         mScr = new CWsScreenDevice(mWs);
       
   432     } catch (const std::bad_alloc &) {
       
   433         mWs.Close();
       
   434         return false;
       
   435     }
       
   436     mScr->Construct();
       
   437     mGroup = RWindowGroup(mWs);
       
   438     mGroup.Construct(1, ETrue);
       
   439     mWin = RWindow(mWs);
       
   440     mWin.Construct(mGroup, 2);
       
   441     mWin.SetExtent(TPoint(0, 0), mScr->SizeInPixels());
       
   442     mWin.EnableVisibilityChangeEvents();
       
   443 
       
   444 #ifdef HB_SPLASH_VERBOSE_LOGGING
       
   445     RDebug::Printf("[hbsplash] wserv init took %d ms", (int) t.restart());
       
   446 #endif
       
   447 
       
   448     // Do not return failure when egl/openvg calls fail. Let it go, we
       
   449     // just won't see the splash in such a case, and that's fine.
       
   450     mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
       
   451     eglInitialize(mDisplay, 0, 0);
       
   452     eglBindAPI(EGL_OPENVG_API);
       
   453     EGLConfig config;
       
   454     EGLint numConfigs;
       
   455     const EGLint attribList[] = {
       
   456         EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
       
   457         EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_VG_ALPHA_FORMAT_PRE_BIT,
       
   458         EGL_RED_SIZE, 8,
       
   459         EGL_GREEN_SIZE, 8,
       
   460         EGL_BLUE_SIZE, 8,
       
   461         EGL_ALPHA_SIZE, 8,
       
   462         EGL_NONE
       
   463     };
       
   464     eglChooseConfig(mDisplay, attribList, &config, 1, &numConfigs);
       
   465     mContext = eglCreateContext(mDisplay, config, EGL_NO_CONTEXT, 0);
       
   466     if (mContext == EGL_NO_CONTEXT) {
       
   467         RDebug::Printf("[hbsplash] eglCreateContext failed (%d)", eglGetError());
       
   468     }
       
   469     const EGLint attribList2[] = {
       
   470         EGL_VG_ALPHA_FORMAT, EGL_VG_ALPHA_FORMAT_PRE,
       
   471         EGL_NONE
       
   472     };
       
   473     mSurface = eglCreateWindowSurface(mDisplay, config,
       
   474                                       (EGLNativeWindowType)(&mWin), attribList2);
       
   475     if (mSurface == EGL_NO_SURFACE) {
       
   476         RDebug::Printf("[hbsplash] eglCreateWindowSurface failed (%d)", eglGetError());
       
   477     }
       
   478 
       
   479 #ifdef HB_SPLASH_VERBOSE_LOGGING
       
   480     RDebug::Printf("[hbsplash] egl+openvg init took %d ms", (int) t.elapsed());
       
   481 #endif
       
   482 
       
   483     return true;
       
   484 }
       
   485 
       
   486 void HbSplashScreenSymbianVg::start(HbSplashScreen::Flags flags)
       
   487 {
       
   488     if (!mImageData) {
       
   489         int w, h, bpl;
       
   490         QImage::Format fmt;
       
   491         RequestProps *props = requestProps();
       
   492 #ifdef HB_SPLASH_VERBOSE_LOGGING
       
   493         ELAPSED_TIMER t;
       
   494         t.start();
       
   495 #endif
       
   496         // Start loading the splash screen data but don't wait until it's
       
   497         // done. Instead, move on to graphics initialization.
       
   498         uchar *asyncHandle = HbSplash::load(w, h, bpl, fmt, flags,
       
   499                                             props->mAppId, props->mScreenId,
       
   500                                             0, 0, true);
       
   501 #ifdef HB_SPLASH_VERBOSE_LOGGING
       
   502         RDebug::Printf("[hbsplash] async load start took %d ms", (int) t.restart());
       
   503 #endif
       
   504 
       
   505         if (!mInited) {
       
   506             if (init()) {
       
   507                 mInited = true;
       
   508             } else {
       
   509                 RDebug::Printf("[hbsplash] init() failed");
       
   510                 return;
       
   511             }
       
   512         }
       
   513 
       
   514 #ifdef HB_SPLASH_VERBOSE_LOGGING
       
   515         t.restart();
       
   516 #endif
       
   517         // The image data and its properties will shortly be needed so retrieve
       
   518         // the result of the load operation.
       
   519         mImageData = HbSplash::finishAsync(asyncHandle);
       
   520 #ifdef HB_SPLASH_VERBOSE_LOGGING
       
   521         RDebug::Printf("[hbsplash] finishAsync() took %d ms", (int) t.restart());
       
   522 #endif
       
   523 
       
   524         if (mImageData) {
       
   525             eglMakeCurrent(mDisplay, mSurface, mSurface, mContext);
       
   526             VGImage img = vgCreateImage(VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER);
       
   527             if (img != VG_INVALID_HANDLE) {
       
   528                 vgImageSubData(img, mImageData, bpl, VG_sARGB_8888_PRE, 0, 0, w, h);
       
   529             } else {
       
   530                 RDebug::Printf("[hbsplash] vgCreateImage failed (%d)", vgGetError());
       
   531             }
       
   532 
       
   533 #ifdef HB_SPLASH_VERBOSE_LOGGING
       
   534             RDebug::Printf("[hbsplash] image init took %d ms", (int) t.restart());
       
   535 #endif
       
   536 
       
   537             mWin.Activate();
       
   538             mWin.Invalidate();
       
   539             mWin.BeginRedraw();
       
   540 
       
   541 #ifdef HB_SPLASH_VERBOSE_LOGGING
       
   542             RDebug::Printf("[hbsplash] redraw init took %d ms", (int) t.restart());
       
   543 #endif
       
   544 
       
   545             vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
       
   546             VGfloat mat[9];
       
   547             mat[0] = 1.0f;
       
   548             mat[1] = 0.0f;
       
   549             mat[2] = 0.0f;
       
   550             mat[3] = 0.0f;
       
   551             mat[4] = -1.0f;
       
   552             mat[5] = 0.0f;
       
   553             mat[6] = 0.0f;
       
   554             mat[7] = h;
       
   555             mat[8] = 1.0f;
       
   556             vgLoadMatrix(mat);
       
   557             vgDrawImage(img);
       
   558             vgDestroyImage(img);
       
   559             eglSwapBuffers(mDisplay, mSurface);
       
   560 
       
   561             mWin.EndRedraw();
       
   562             mWs.Flush();
       
   563 
       
   564 #ifdef HB_SPLASH_VERBOSE_LOGGING
       
   565             RDebug::Printf("[hbsplash] drawing took %d ms", (int) t.elapsed());
       
   566 #endif
       
   567 
       
   568             bool isQtAvailable = QApplication::instance() != 0;
       
   569 #ifdef HB_SPLASH_VERBOSE_LOGGING
       
   570             RDebug::Printf("[hbsplash] qapplication present: %d", isQtAvailable);
       
   571 #endif
       
   572             if (isQtAvailable) {
       
   573                 doQtPhase();
       
   574             }
       
   575             // If there is no QApplication then there is no active scheduler,
       
   576             // cone, etc. either so defer the creation of the timer and the
       
   577             // visibility listener. doQtPhase() will be called later by
       
   578             // HbApplication when QApplication is constructed. If HbApplication
       
   579             // is not used at all and start() is called before instantiating
       
   580             // QApplication then doQtPhase() is never called but there is not
       
   581             // much we can do and it is not mandatory anyway.
       
   582 
       
   583         } else {
       
   584             HbSplashScreen::destroy();
       
   585         }
       
   586     }
       
   587 }
       
   588 
       
   589 TInt HbSplashScreenSymbianVg::timerCallback(TAny *param)
       
   590 {
       
   591     HbSplashScreenSymbianVg *self = static_cast<HbSplashScreenSymbianVg *>(param);
       
   592     self->mTimer->Cancel();
       
   593     RDebug::Printf("[hbsplash] timeout while splash screen is active");
       
   594     HbSplashScreen::destroy();
       
   595     return 0;
       
   596 }
       
   597 
       
   598 void HbSplashScreenSymbianVg::doQtPhase()
       
   599 {
       
   600 #ifdef HB_SPLASH_VERBOSE_LOGGING
       
   601     RDebug::Printf("[hbsplash] HbSplashScreenSymbianVg::doQtPhase()");
       
   602 #endif
       
   603     // Now there is an active scheduler.
       
   604     if (!mTimer) {
       
   605         TRAPD(err, mTimer = CPeriodic::NewL(CActive::EPriorityStandard));
       
   606         if (err == KErrNone) {
       
   607             TTimeIntervalMicroSeconds32 iv = auto_stop_interval * 1000;
       
   608             mTimer->Start(iv, iv, TCallBack(timerCallback, this));
       
   609         }
       
   610     }
       
   611     // Here we could start a listener for visibility events or similar. But it
       
   612     // is better not to, because we may get an ENotVisible event (in theory)
       
   613     // even when the incoming window has not yet got any real content so the
       
   614     // splash cannot be destroyed at that stage.
       
   615 }
       
   616 
       
   617 #endif // HB_SPLASH_DIRECT_WS