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 |
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 |