src/hbservers/hbsplashgenerator/hbsplashgenerator.cpp
changeset 34 ed14f46c0e55
parent 7 923ff622b8b9
equal deleted inserted replaced
31:7516d6d86cf5 34:ed14f46c0e55
    59 const char *last_theme_key = "lasttheme";
    59 const char *last_theme_key = "lasttheme";
    60 const char *last_lang_key = "lastlang";
    60 const char *last_lang_key = "lastlang";
    61 const char *last_file_count_key = "lastfilecount";
    61 const char *last_file_count_key = "lastfilecount";
    62 const char *last_output_dir_key = "lastoutdir";
    62 const char *last_output_dir_key = "lastoutdir";
    63 
    63 
       
    64 // Using invokeMethod with QueuedConnection is not good enough as it can
       
    65 // cause starvation for Symbian active objects (at least in Qt versions
       
    66 // before 4.7.2) so the theme change notification would get delayed until
       
    67 // an entire regeneration cycle is finished, which is not acceptable. So
       
    68 // use a QTimer with a small timeout instead. A small delay is beneficial
       
    69 // anyway to divide the load a bit better so perf does not drop that much
       
    70 // in apps while regeneration is on-going.
       
    71 #define NEXT_STAGE(obj, func) QTimer::singleShot(10, this, SLOT(func()))
       
    72 
    64 HbSplashGenerator::HbSplashGenerator()
    73 HbSplashGenerator::HbSplashGenerator()
    65     : mMainWindowLocked(false),
    74     : mMainWindowLocked(false),
    66       mProcessQueuePending(false),
    75       mProcessQueuePending(false),
    67       mForceRegen(false),
    76       mForceRegen(false),
    68       mMainWindow(0),
    77       mMainWindow(0),
    69       mFirstRegenerate(true)
    78       mFirstRegenerate(true),
       
    79       mSaveSplFailed(false)
    70 {
    80 {
    71 #if defined(Q_OS_SYMBIAN)
    81 #if defined(Q_OS_SYMBIAN)
    72     CCoeEnv::Static()->FsSession().CreatePrivatePath(EDriveC);
    82     CCoeEnv::Static()->FsSession().CreatePrivatePath(EDriveC);
    73     QString iniFileName = QString("c:/private/%1/hbsplashgen.ini")
    83     QString iniFileName = QString("c:/private/%1/hbsplashgen.ini")
    74                           .arg(QString::number(hbsplash_server_uid3.iUid, 16));
    84                           .arg(QString::number(hbsplash_server_uid3.iUid, 16));
   105 
   115 
   106 static void log(const QString &msg, const QString &theme = QString(), int orientation = -1)
   116 static void log(const QString &msg, const QString &theme = QString(), int orientation = -1)
   107 {
   117 {
   108     const char *fmt = PRE " %s ('%s' '%s')";
   118     const char *fmt = PRE " %s ('%s' '%s')";
   109     QString oriName = orientationName(static_cast<Qt::Orientation>(orientation));
   119     QString oriName = orientationName(static_cast<Qt::Orientation>(orientation));
   110     qDebug(fmt, qPrintable(msg), qPrintable(theme), qPrintable(oriName));
   120     splDebug(fmt, qPrintable(msg), qPrintable(theme), qPrintable(oriName));
   111 }
   121 }
   112 
   122 
   113 // To be called on startup and after each fully completed regeneration.
   123 // To be called on startup and after each fully completed regeneration.
   114 // Returns the number of files in the output directory.
   124 // Returns the number of files in the output directory.
   115 int HbSplashGenerator::updateOutputDirContents(const QString &outDir)
   125 int HbSplashGenerator::updateOutputDirContents(const QString &outDir)
   121 }
   131 }
   122 
   132 
   123 void HbSplashGenerator::start(bool forceRegen)
   133 void HbSplashGenerator::start(bool forceRegen)
   124 {
   134 {
   125     mForceRegen = forceRegen;
   135     mForceRegen = forceRegen;
       
   136     // Let's have a certain delay for better load balancing during
       
   137     // boot. It is not strictly required, though.
   126     QTimer::singleShot(5000, this, SLOT(doStart()));
   138     QTimer::singleShot(5000, this, SLOT(doStart()));
   127 }
   139 }
   128 
   140 
   129 void HbSplashGenerator::doStart()
   141 void HbSplashGenerator::doStart()
   130 {
   142 {
   131     qDebug() << PRE << "accessing theme";
   143     splDeb() << PRE << "accessing theme";
   132     // Start listening to the theme-change-finished signal.
   144     // Start listening to the theme-change-finished signal.
   133     HbTheme *theme = hbInstance->theme();
   145     HbTheme *theme = hbInstance->theme();
   134     connect(theme, SIGNAL(changeFinished()), SLOT(regenerate()));
   146     connect(theme, SIGNAL(changeFinished()), SLOT(regenerate()));
   135 
   147 
   136     // Watch also the directories containing splashml files. Files may
   148     // Watch also the directories containing splashml files. Files may
   142         if (QDir(dir).exists()) {
   154         if (QDir(dir).exists()) {
   143             mFsWatcher.addPath(dir);
   155             mFsWatcher.addPath(dir);
   144         }
   156         }
   145     }
   157     }
   146 
   158 
       
   159     // Regenerate screens, if needed.
       
   160     scheduleRegen();
       
   161 }
       
   162 
       
   163 void HbSplashGenerator::scheduleRegen()
       
   164 {
   147     // Regenerate screens on startup only when the theme, the language, the
   165     // Regenerate screens on startup only when the theme, the language, the
   148     // number of files in the splash screen directory, or the splash screen
   166     // number of files in the splash screen directory, or the splash screen
   149     // directory path is different than the recorded values. (or when
   167     // directory path is different than the recorded values. (or when
   150     // regeneration is forced via command line arg)
   168     // regeneration is forced via command line arg)
   151     QString lastTheme = mSettings->value(QLatin1String(last_theme_key)).toString();
   169     QString lastTheme = mSettings->value(QLatin1String(last_theme_key)).toString();
   152     QString lastLang = mSettings->value(QLatin1String(last_lang_key)).toString();
   170     QString lastLang = mSettings->value(QLatin1String(last_lang_key)).toString();
   153     int lastFileCount = mSettings->value(QLatin1String(last_file_count_key)).toInt();
   171     int lastFileCount = mSettings->value(QLatin1String(last_file_count_key)).toInt();
   154     QString lastOutputDir = mSettings->value(QLatin1String(last_output_dir_key)).toString();
   172     QString lastOutputDir = mSettings->value(QLatin1String(last_output_dir_key)).toString();
   155     QString currentTheme = theme->name();
   173     QString currentTheme = hbInstance->theme()->name();
   156     QString currentLang = QLocale::system().name();
   174     QString currentLang = QLocale::system().name();
   157     QString currentOutputDir = hbsplash_output_dir();
   175     QString currentOutputDir = hbsplash_output_dir();
   158     int currentFileCount = updateOutputDirContents(currentOutputDir);
   176     int currentFileCount = updateOutputDirContents(currentOutputDir);
   159     qDebug() << PRE << "last regen:" << lastTheme << lastLang << lastFileCount << lastOutputDir
   177     qDebug() << PRE << "last regen:" << lastTheme << lastLang << lastFileCount << lastOutputDir
   160              << "current:" << currentTheme << currentLang << currentFileCount << currentOutputDir;
   178              << "current:" << currentTheme << currentLang << currentFileCount << currentOutputDir;
   162             || currentFileCount == 0 // not having any files is wrong for sure
   180             || currentFileCount == 0 // not having any files is wrong for sure
   163             || currentTheme != lastTheme
   181             || currentTheme != lastTheme
   164             || currentLang != lastLang
   182             || currentLang != lastLang
   165             || currentFileCount != lastFileCount
   183             || currentFileCount != lastFileCount
   166             || currentOutputDir != lastOutputDir) {
   184             || currentOutputDir != lastOutputDir) {
   167         QMetaObject::invokeMethod(this, "regenerate", Qt::QueuedConnection);
   185         NEXT_STAGE(this, regenerate);
       
   186         mForceRegen = false;
   168     }
   187     }
   169 }
   188 }
   170 
   189 
   171 void HbSplashGenerator::uncachedRegenerate()
   190 void HbSplashGenerator::uncachedRegenerate()
   172 {
   191 {
   173     // Same as regenerate() but no caching is used so every file is
   192     // Same as regenerate() but no caching is used so every file is
   174     // parsed again.
   193     // parsed again.
   175     mParsedSplashmls.clear();
   194     mParsedSplashmls.clear();
   176     regenerate();
   195     regenerate();
       
   196 }
       
   197 
       
   198 inline void delete_splash_screens()
       
   199 {
       
   200     QDir outDir(hbsplash_output_dir());
       
   201     if (outDir.exists()) {
       
   202         QStringList names = outDir.entryList(QStringList() << "*", QDir::Files);
       
   203         foreach(const QString & name, names) {
       
   204             outDir.remove(name);
       
   205         }
       
   206     }
   177 }
   207 }
   178 
   208 
   179 void HbSplashGenerator::regenerate()
   209 void HbSplashGenerator::regenerate()
   180 {
   210 {
   181     QString themeName = hbInstance->theme()->name();
   211     QString themeName = hbInstance->theme()->name();
   184         try {
   214         try {
   185             emit regenerateStarted();
   215             emit regenerateStarted();
   186             QTime queuePrepTime;
   216             QTime queuePrepTime;
   187             queuePrepTime.start();
   217             queuePrepTime.start();
   188             // Delete existing splash screens. This is important because apps
   218             // Delete existing splash screens. This is important because apps
   189             // should never pick up a screen with the previous theme or
   219             // should never pick up a screen with the previous theme or language
   190             // language. If the generation of the new screens (at least the
   220             // (not even when generating screens with the new theme fails). If
   191             // empty view) has not finished when a new app is started then it is
   221             // the generation of the new screens (at least the empty view) has
   192             // better to show no splash screen at all.
   222             // not finished when a new app is started then it is better to show
   193             QDir outDir(hbsplash_output_dir());
   223             // no splash screen at all.
   194             if (outDir.exists()) {
   224             delete_splash_screens();
   195                 QStringList names = outDir.entryList(QStringList() << "*", QDir::Files);
       
   196                 foreach(const QString & name, names) {
       
   197                     outDir.remove(name);
       
   198                 }
       
   199             }
       
   200             // Clear the queue, generating screens with a non-current theme is
   225             // Clear the queue, generating screens with a non-current theme is
   201             // not possible anyway.
   226             // not possible anyway.
   202             mQueue.clear();
   227             mQueue.clear();
   203             // If this is the first invocation then put some requests for
   228             // If this is the first invocation then put some requests for
   204             // screens we won't use. On certain platforms the very first
   229             // screens we won't use. On certain platforms the very first
   212             // Queue the screenshot request for both orientations.
   237             // Queue the screenshot request for both orientations.
   213             mQueue.enqueue(QueueItem(themeName, Qt::Vertical));
   238             mQueue.enqueue(QueueItem(themeName, Qt::Vertical));
   214             mQueue.enqueue(QueueItem(themeName, Qt::Horizontal));
   239             mQueue.enqueue(QueueItem(themeName, Qt::Horizontal));
   215             queueAppSpecificItems(themeName, Qt::Vertical);
   240             queueAppSpecificItems(themeName, Qt::Vertical);
   216             queueAppSpecificItems(themeName, Qt::Horizontal);
   241             queueAppSpecificItems(themeName, Qt::Horizontal);
   217             qDebug() << PRE << "queue preparation time (ms):" << queuePrepTime.elapsed();
   242             mSaveSplFailed = false;
   218             QMetaObject::invokeMethod(this, "processQueue", Qt::QueuedConnection);
   243             // If a previous regenerate is on-going, the mainwindow is locked
       
   244             // and we will not get to process the new queue entries before the
       
   245             // one screen, that is in progress, is done. However that one last
       
   246             // screen must not be rendered and written to file. To indicate
       
   247             // this, use a flag in the first queue entry.
       
   248             //
       
   249             // Note that removing the metacalls or timers is not an option as
       
   250             // that would lead to possibly leaving the mainwindow locked (and we
       
   251             // must never unlock it "manually" as it may be locked by some other
       
   252             // entity, e.g. the statusbar generator), so we have to let the last
       
   253             // screen to finish normally (excluding rendering and storage).
       
   254             mQueue.head().mFirstInRegen = true;
       
   255             splDeb() << PRE << "queue preparation time (ms):" << queuePrepTime.elapsed();
       
   256             NEXT_STAGE(this, processQueue);
   219         } catch (const std::bad_alloc &) {
   257         } catch (const std::bad_alloc &) {
   220             cleanup();
   258             cleanup();
   221         }
   259         }
   222     }
   260     }
   223 }
   261 }
   224 
   262 
       
   263 // This function is for the splashviewer tool only, do not use from elsewhere.
   225 void HbSplashGenerator::regenerateOne(const QString &splashmlFileName, const QString &customTrDir)
   264 void HbSplashGenerator::regenerateOne(const QString &splashmlFileName, const QString &customTrDir)
   226 {
   265 {
   227     mQueue.clear();
   266     mQueue.clear();
   228     QueueItem item(hbInstance->theme()->name(), Qt::Vertical);
   267     QueueItem item(hbInstance->theme()->name(), Qt::Vertical);
   229     QString path = QFileInfo(splashmlFileName).path();
   268     QString path = QFileInfo(splashmlFileName).path();
   234     parseSplashml(splashmlFileName, item);
   273     parseSplashml(splashmlFileName, item);
   235     item.mDocmlFileName = QDir(path).filePath(item.mDocmlFileName);
   274     item.mDocmlFileName = QDir(path).filePath(item.mDocmlFileName);
   236     mQueue.enqueue(item); // generate it regardless of the fixed orientation setting
   275     mQueue.enqueue(item); // generate it regardless of the fixed orientation setting
   237     item.mOrientation = Qt::Horizontal;
   276     item.mOrientation = Qt::Horizontal;
   238     mQueue.enqueue(item);
   277     mQueue.enqueue(item);
   239     QMetaObject::invokeMethod(this, "processQueue", Qt::QueuedConnection);
   278     NEXT_STAGE(this, processQueue);
   240 }
   279 }
   241 
   280 
   242 QImage HbSplashGenerator::renderView()
   281 QImage HbSplashGenerator::renderView()
   243 {
   282 {
   244     log("renderView()", mItem.mThemeName, mItem.mOrientation);
   283     log("renderView()", mItem.mThemeName, mItem.mOrientation);
   255     // these images)
   294     // these images)
   256     QImage image(mMainWindow->size(), QImage::Format_ARGB32_Premultiplied);
   295     QImage image(mMainWindow->size(), QImage::Format_ARGB32_Premultiplied);
   257     image.fill(QColor(Qt::transparent).rgba());
   296     image.fill(QColor(Qt::transparent).rgba());
   258     QPainter painter(&image);
   297     QPainter painter(&image);
   259     mMainWindow->render(&painter);
   298     mMainWindow->render(&painter);
   260     qDebug() << PRE << "rendering time (ms):" << t.elapsed();
   299     splDeb() << PRE << "rendering time (ms):" << t.elapsed();
   261     return image;
   300     return image;
   262 }
   301 }
   263 
   302 
   264 void HbSplashGenerator::processQueue()
   303 void HbSplashGenerator::processQueue()
   265 {
   304 {
   266     qDebug() << PRE << "processQueue()";
   305     splDeb() << PRE << "processQueue()";
   267     // If the queue is empty then the splash regeneraton is complete so store
   306     // If the queue is empty then the splash regeneraton is complete so store
   268     // the current theme and language names as the last fully processed ones in
   307     // the current theme and language names as the last fully processed ones in
   269     // the settings and stop.
   308     // the settings and stop.
   270     if (mQueue.isEmpty()) {
   309     if (mQueue.isEmpty()) {
   271         qDebug() << PRE << "queue is empty  regen finished";
   310         qDebug() << PRE << "queue is empty  regen finished";
   272         mSettings->setValue(last_theme_key, hbInstance->theme()->name());
   311         mSettings->setValue(last_theme_key, hbInstance->theme()->name());
   273         mSettings->setValue(last_lang_key, QLocale::system().name());
   312         mSettings->setValue(last_lang_key, QLocale::system().name());
   274         QString outDir = hbsplash_output_dir();
   313         QString outDir = hbsplash_output_dir();
   275         mSettings->setValue(last_file_count_key, updateOutputDirContents(outDir));
   314         // Notify the server and get the number of generated files...
       
   315         int fileCount = updateOutputDirContents(outDir);
       
   316         // ...but store zero if some file writing failed at some point
       
   317         // so there will be a regeneration on next boot at least.
       
   318         if (mSaveSplFailed) {
       
   319             qWarning() << PRE << "some files not ok, ignoring file count";
       
   320             fileCount = 0;
       
   321             // Waiting until next boot is not always the best solution so try
       
   322             // again a bit later. This has to be limited, though, to prevent
       
   323             // continously flooding the system with regenerate requests in case
       
   324             // of an unusable drive. So retry only for a limited number of
       
   325             // times, if all else fails we will try again on next boot.
       
   326             static int retriesLeft = 3;
       
   327             if (retriesLeft-- > 0) {
       
   328                 QTimer::singleShot(60000, this, SLOT(scheduleRegen())); // 1 min
       
   329             }
       
   330         } else {
       
   331             splDeb() << PRE << "all files ok";
       
   332         }
       
   333         mSettings->setValue(last_file_count_key, fileCount);
   276         mSettings->setValue(last_output_dir_key, outDir);
   334         mSettings->setValue(last_output_dir_key, outDir);
   277         emit finished();
   335         emit finished();
   278         qDebug() << PRE << "processQueue() over";
   336         splDeb() << PRE << "processQueue() over";
   279         return;
   337         return;
   280     }
   338     }
   281     // If a previous splash generation is still in progress or a compositor is
   339     // If a previous splash generation is still in progress or a compositor is
   282     // working then do nothing.
   340     // working then do nothing.
   283     if (!lockMainWindow()) {
   341     if (!lockMainWindow()) {
   284         mProcessQueuePending = true;
   342         mProcessQueuePending = true;
   285         qDebug() << PRE << "still busy  processQueue() over";
   343         splDeb() << PRE << "still busy  processQueue() over";
   286         return;
   344         return;
   287     }
   345     }
   288     try {
   346     try {
   289         mProcessQueuePending = false;
   347         mProcessQueuePending = false;
   290         mItem = mQueue.dequeue();
   348         mItem = mQueue.dequeue();
   291         mItemTime.start();
   349         mItemTime.start();
   292         log("generating splash screen", mItem.mThemeName, mItem.mOrientation);
   350         log("generating splash screen", mItem.mThemeName, mItem.mOrientation);
   293 
   351 
   294         ensureMainWindow();
   352         ensureMainWindow();
   295         mMainWindow->setOrientation(mItem.mOrientation, false);
   353         mMainWindow->setOrientation(mItem.mOrientation, false);
   296         qDebug() << PRE << "mainwindow init time (ms):" << mItemTime.elapsed();
   354         splDeb() << PRE << "mainwindow init time (ms):" << mItemTime.elapsed();
   297 
   355 
   298         QTime setupTime;
   356         QTime setupTime;
   299         setupTime.start();
   357         setupTime.start();
   300         setupAppSpecificWindow();
   358         setupAppSpecificWindow();
   301         finishWindow();
   359         splDeb() << PRE << "content setup time (ms):" << setupTime.elapsed();
   302         qDebug() << PRE << "content setup time(ms):" << setupTime.elapsed();
   360 
   303 
   361         // The async call chain goes like this:
   304         QMetaObject::invokeMethod(this, "processWindow", Qt::QueuedConnection);
   362         // processQueue -> finishWindow -> processWindow -> processQueue -> ...
       
   363         // finishWindow() cannot be called directly from here because that would
       
   364         // result in asserts in QGraphicsScene with certain Qt versions.
       
   365         NEXT_STAGE(this, finishWindow);
   305 
   366 
   306     } catch (const std::bad_alloc &) {
   367     } catch (const std::bad_alloc &) {
   307         cleanup();
   368         cleanup();
   308     }
   369     }
   309     qDebug() << PRE << "processQueue() over";
   370     splDeb() << PRE << "processQueue() over";
   310 }
   371 }
   311 
   372 
   312 HbMainWindow *HbSplashGenerator::ensureMainWindow()
   373 HbMainWindow *HbSplashGenerator::ensureMainWindow()
   313 {
   374 {
   314     if (!mMainWindow) {
   375     if (!mMainWindow) {
   315         // The FixedVertical flag is used just to disable the sensor-based
   376         // The FixedVertical flag is used just to disable the sensor-based
   316         // orientation switching.
   377         // orientation switching.
   317         mMainWindow = new HbMainWindow(0, Hb::WindowFlagFixedVertical);
   378         mMainWindow = new HbMainWindow(0, Hb::WindowFlagFixedVertical);
   318         // Make sure that at least the 1st phase of the delayed
   379         // Make sure that at least the 1st phase of the delayed
   319         // construction is done right now.
   380         // construction is done right now.
   320         HbMainWindowPrivate::d_ptr(mMainWindow)->_q_delayedConstruction();
   381         HbMainWindowPrivate *mwd = HbMainWindowPrivate::d_ptr(mMainWindow);
       
   382         mwd->_q_delayedConstruction();
       
   383         mwd->mStatusBar->startClockTimer(); // may not start otherwise as it gets no visibility events
   321     }
   384     }
   322     return mMainWindow;
   385     return mMainWindow;
   323 }
   386 }
   324 
   387 
   325 void HbSplashGenerator::processWindow()
   388 void HbSplashGenerator::processWindow()
   326 {
   389 {
   327     // Take the screenshot, remove content, and move on to the next request in the queue.
   390     // Take the screenshot, remove content, and move on to the next request in the queue.
   328     log("processWindow()  rendering splash screen", mItem.mThemeName, mItem.mOrientation);
   391     if (!newRegenPending()) {
   329     takeScreenshot();
   392         log("processWindow()  rendering splash screen", mItem.mThemeName, mItem.mOrientation);
   330     qDebug() << PRE << "total time for screen (ms):" << mItemTime.elapsed();
   393         takeScreenshot();
       
   394         splDeb() << PRE << "total time for screen (ms):" << mItemTime.elapsed();
       
   395     }
   331 
   396 
   332     QList<HbView *> views = mMainWindow->views();
   397     QList<HbView *> views = mMainWindow->views();
   333     foreach(HbView * view, views) {
   398     foreach(HbView * view, views) {
   334         mMainWindow->removeView(view);
   399         mMainWindow->removeView(view);
   335         delete view;
   400         delete view;
   336     }
   401     }
   337     clearTranslators();
   402     clearTranslators();
   338 
   403 
   339     unlockMainWindowInternal();
   404     unlockMainWindowInternal();
   340     QMetaObject::invokeMethod(this, "processQueue", Qt::QueuedConnection);
   405     NEXT_STAGE(this, processQueue);
   341     log("processWindow() over", mItem.mThemeName, mItem.mOrientation);
   406     log("processWindow() over", mItem.mThemeName, mItem.mOrientation);
   342 }
   407 }
   343 
   408 
   344 void HbSplashGenerator::takeScreenshot()
   409 void HbSplashGenerator::takeScreenshot()
   345 {
   410 {
   354         // change.
   419         // change.
   355         QImage image = renderView();
   420         QImage image = renderView();
   356         QTime t;
   421         QTime t;
   357         t.start();
   422         t.start();
   358         QString splashFile = splashFileName();
   423         QString splashFile = splashFileName();
   359         qDebug() << PRE << "saving to" << splashFile;
   424         splDeb() << PRE << "saving to" << splashFile;
   360         if (saveSpl(splashFile, image, mItem.mFlagsToStore)) {
   425         if (saveSpl(splashFile, image, mItem.mFlagsToStore)) {
   361 #if !defined(Q_OS_SYMBIAN) && defined(QT_DEBUG)
   426 #if !defined(Q_OS_SYMBIAN) && defined(QT_DEBUG)
   362             image.save(splashFile + QLatin1String(".png"));
   427             image.save(splashFile + QLatin1String(".png"));
   363 #endif
   428 #endif
   364         } else {
   429         } else {
   365             qWarning() << PRE << "file write failed for" << splashFile;
   430             qWarning() << PRE << "file write failed for" << splashFile;
   366         }
   431             mSaveSplFailed = true;
   367         qDebug() << PRE << "save time (ms):" << t.elapsed();
   432             // After setting the fail flag, clear the queue to stop processing
       
   433             // further screens because file writes would probably fail anyway.
       
   434             // Instead, processQueue() will schedule a retry at a later time.
       
   435             mQueue.clear();
       
   436         }
       
   437         splDeb() << PRE << "save time (ms):" << t.elapsed();
   368         log("takeScreenshot() over", mItem.mThemeName, mItem.mOrientation);
   438         log("takeScreenshot() over", mItem.mThemeName, mItem.mOrientation);
   369     } catch (const std::bad_alloc &) {
   439     } catch (const std::bad_alloc &) {
   370         cleanup();
   440         cleanup();
   371     }
   441     }
   372 }
   442 }
   373 
   443 
   374 QString HbSplashGenerator::splashFileName()
   444 QString HbSplashGenerator::splashFileName()
   375 {
   445 {
   376     QString outDirName = hbsplash_output_dir();
   446     QString outDirName = hbsplash_output_dir();
   377     QDir dir(outDirName);
   447     QDir dir(outDirName);
       
   448 #ifdef Q_OS_SYMBIAN
       
   449     // Do not use QDir::mkpath() on Symbian. It is not able to create the
       
   450     // 'private' directory itself in case it does not exist (which is possible
       
   451     // during first boot because splashgen is started relatively early and the
       
   452     // eMMC may be totally empty at that point). RFs::MkDirAll() works better in
       
   453     // this respect.
       
   454     QString nativeOutPath = QDir::toNativeSeparators(outDirName);
       
   455     if (!nativeOutPath.endsWith('\\')) {
       
   456         nativeOutPath.append('\\');
       
   457     }
       
   458     TPtrC nativeOutPathDes(static_cast<const TUint16 *>(nativeOutPath.utf16()),
       
   459                            nativeOutPath.length());
       
   460     TInt err = CCoeEnv::Static()->FsSession().MkDirAll(nativeOutPathDes);
       
   461     if (err != KErrNone && err != KErrAlreadyExists) {
       
   462         qWarning() << PRE << "MkDirAll failed with" << err << "for" << nativeOutPath;
       
   463     }
       
   464 #else
   378     if (!dir.exists()) {
   465     if (!dir.exists()) {
   379         if (!QDir(".").mkdir(outDirName)) {
   466         if (!QDir(".").mkpath(outDirName)) {
   380             qWarning() << PRE << "mkdir failed for" << outDirName;
   467             qWarning() << PRE << "mkpath failed for" << outDirName;
   381         }
   468         }
   382     }
   469     }
       
   470 #endif
   383     // "splash_<orientation>_<appid>_<screenid>"
   471     // "splash_<orientation>_<appid>_<screenid>"
   384     QString splashFile = dir.filePath("splash_");
   472     QString splashFile = dir.filePath("splash_");
   385     splashFile.append(orientationName(mItem.mOrientation));
   473     splashFile.append(orientationName(mItem.mOrientation));
   386     if (!mItem.mAppId.isEmpty()) {
   474     if (!mItem.mAppId.isEmpty()) {
   387         splashFile.append('_');
   475         splashFile.append('_');
   407         f.write((char *) &w, sizeof(quint32));
   495         f.write((char *) &w, sizeof(quint32));
   408         f.write((char *) &h, sizeof(quint32));
   496         f.write((char *) &h, sizeof(quint32));
   409         f.write((char *) &bpl, sizeof(quint32));
   497         f.write((char *) &bpl, sizeof(quint32));
   410         f.write((char *) &fmt, sizeof(qint32));
   498         f.write((char *) &fmt, sizeof(qint32));
   411         f.write((char *) &extra, sizeof(quint32));
   499         f.write((char *) &extra, sizeof(quint32));
   412         f.write((const char *) image.bits(), bpl * h);
   500 #ifdef HB_SPLASH_COMPRESSION
       
   501         QTime t;
       
   502         t.start();
       
   503         QByteArray compData = qCompress((const uchar *) image.bits(), bpl * h);
       
   504         quint32 len = compData.size();
       
   505         splDeb() << PRE << "compressed" << bpl * h << "to" << len << "in" << t.elapsed() << "ms";
       
   506         f.write((char *) &len, sizeof(quint32));
       
   507         qint64 wcount = f.write(compData.constData(), len);
       
   508 #else
       
   509         quint32 len = 0; // 0 compressed length indicates uncompressed data
       
   510         f.write((char *) &len, sizeof(quint32));
       
   511         len = bpl * h;
       
   512         qint64 wcount = f.write((const char *) image.bits(), len);
       
   513 #endif
   413         f.close();
   514         f.close();
   414         return true;
   515         return wcount == len;
   415     }
   516     }
   416     return false;
   517     return false;
   417 }
   518 }
   418 
   519 
   419 void HbSplashGenerator::cleanup()
   520 void HbSplashGenerator::cleanup()
   441 }
   542 }
   442 
   543 
   443 HbSplashGenerator::QueueItem::QueueItem()
   544 HbSplashGenerator::QueueItem::QueueItem()
   444     : mOrientation(Qt::Vertical),
   545     : mOrientation(Qt::Vertical),
   445       mHideBackground(false),
   546       mHideBackground(false),
   446       mFlagsToStore(0)
   547       mFlagsToStore(0),
       
   548       mFirstInRegen(false)
   447 {
   549 {
   448 }
   550 }
   449 
   551 
   450 HbSplashGenerator::QueueItem::QueueItem(const QString &themeName, Qt::Orientation orientation)
   552 HbSplashGenerator::QueueItem::QueueItem(const QString &themeName, Qt::Orientation orientation)
   451     : mThemeName(themeName),
   553     : mThemeName(themeName),
   452       mOrientation(orientation),
   554       mOrientation(orientation),
   453       mHideBackground(false),
   555       mHideBackground(false),
   454       mFlagsToStore(0)
   556       mFlagsToStore(0),
       
   557       mFirstInRegen(false)
   455 {
   558 {
   456 }
   559 }
   457 
   560 
   458 void HbSplashGenerator::addSplashmlItemToQueue(const QueueItem &item)
   561 void HbSplashGenerator::addSplashmlItemToQueue(const QueueItem &item)
   459 {
   562 {
   480         QStringList entries = dir.entryList(QStringList() << "*.splashml", QDir::Files);
   583         QStringList entries = dir.entryList(QStringList() << "*.splashml", QDir::Files);
   481         foreach(const QString & entry, entries) {
   584         foreach(const QString & entry, entries) {
   482             // Skip if a file with the same name has already been processed from
   585             // Skip if a file with the same name has already been processed from
   483             // a different location.
   586             // a different location.
   484             if (processedFileNames.contains(entry)) {
   587             if (processedFileNames.contains(entry)) {
   485                 qDebug() << PRE << "skipping splashml (already found at other location)" << dir.filePath(entry);
   588                 splDeb() << PRE << "skipping splashml (already found at other location)" << dir.filePath(entry);
   486                 continue;
   589                 continue;
   487             }
   590             }
   488             processedFileNames.insert(entry);
   591             processedFileNames.insert(entry);
   489             QString fullName = dir.filePath(entry);
   592             QString fullName = dir.filePath(entry);
   490             qDebug() << PRE << "parsing splashml" << fullName;
   593             splDeb() << PRE << "parsing splashml" << fullName;
   491             if (mParsedSplashmls.contains(fullName)) {
   594             if (mParsedSplashmls.contains(fullName)) {
   492                 QueueItem item(mParsedSplashmls.value(fullName));
   595                 QueueItem item(mParsedSplashmls.value(fullName));
   493                 item.mThemeName = themeName;
   596                 item.mThemeName = themeName;
   494                 item.mOrientation = orientation;
   597                 item.mOrientation = orientation;
   495                 qDebug() << PRE << "splashml already parsed  queuing request" << item;
   598                 splDeb() << PRE << "splashml already parsed  queuing request" << item;
   496                 addSplashmlItemToQueue(item);
   599                 addSplashmlItemToQueue(item);
   497                 continue;
   600                 continue;
   498             }
   601             }
   499             QueueItem item(themeName, orientation);
   602             QueueItem item(themeName, orientation);
   500             bool ok = parseSplashml(fullName, item);
   603             bool ok = parseSplashml(fullName, item);
   503                     && !item.mDocmlWidgetName.isEmpty()
   606                     && !item.mDocmlWidgetName.isEmpty()
   504                     && !item.mDocmlFileName.isEmpty()) {
   607                     && !item.mDocmlFileName.isEmpty()) {
   505                 // Add the full path to the filename. The docml is supposed to
   608                 // Add the full path to the filename. The docml is supposed to
   506                 // be in the same directory as the splashml.
   609                 // be in the same directory as the splashml.
   507                 item.mDocmlFileName = dir.filePath(item.mDocmlFileName);
   610                 item.mDocmlFileName = dir.filePath(item.mDocmlFileName);
   508                 qDebug() << PRE << "queuing request" << item;
   611                 splDeb() << PRE << "queuing request" << item;
   509                 addSplashmlItemToQueue(item);
   612                 addSplashmlItemToQueue(item);
   510                 mParsedSplashmls.insert(fullName, item);
   613                 mParsedSplashmls.insert(fullName, item);
   511             } else {
   614             } else {
   512                 qWarning() << PRE << "unable to parse" << fullName;
   615                 qWarning() << PRE << "unable to parse" << fullName;
   513             }
   616             }
   649 
   752 
   650 QObject *CustomDocumentLoader::createObject(const QString &type, const QString &name)
   753 QObject *CustomDocumentLoader::createObject(const QString &type, const QString &name)
   651 {
   754 {
   652     QObject *obj = HbDocumentLoader::createObject(type, name);
   755     QObject *obj = HbDocumentLoader::createObject(type, name);
   653     if (!obj) {
   756     if (!obj) {
   654         qDebug() << PRE << "unsupported object" << type << name;
   757         splDeb() << PRE << "unsupported object" << type << name;
   655         // Cannot let parsing fail because of unknown custom widgets
   758         // Cannot let parsing fail because of unknown custom widgets
   656         // so provide an empty HbWidget (or HbView if the splashml
   759         // so provide an empty HbWidget (or HbView if the splashml
   657         // prefers that).
   760         // prefers that).
   658         if (mItem.mCustomWidgetSubsts.contains(type)) {
   761         if (mItem.mCustomWidgetSubsts.contains(type)) {
   659             QString preferredType = mItem.mCustomWidgetSubsts.value(type);
   762             QString preferredType = mItem.mCustomWidgetSubsts.value(type);
   698         } else if (mItem.mCondSections.contains(lscKey) && mItem.mOrientation == Qt::Horizontal) {
   801         } else if (mItem.mCondSections.contains(lscKey) && mItem.mOrientation == Qt::Horizontal) {
   699             sections << mItem.mCondSections.value(lscKey);
   802             sections << mItem.mCondSections.value(lscKey);
   700         }
   803         }
   701     }
   804     }
   702     sections << mItem.mForcedSections;
   805     sections << mItem.mForcedSections;
   703     qDebug() << PRE << "loading" << mItem.mDocmlFileName << "common section";
   806     splDeb() << PRE << "loading" << mItem.mDocmlFileName << "common section";
   704     bool ok;
   807     bool ok;
   705     loader.load(mItem.mDocmlFileName, &ok);
   808     loader.load(mItem.mDocmlFileName, &ok);
   706     if (ok && !sections.isEmpty()) {
   809     if (ok && !sections.isEmpty()) {
   707         foreach(const QString & section, sections) {
   810         foreach(const QString & section, sections) {
   708             qDebug() << PRE << "loading" << mItem.mDocmlFileName << "section" << section;
   811             splDeb() << PRE << "loading" << mItem.mDocmlFileName << "section" << section;
   709             loader.load(mItem.mDocmlFileName, section, &ok);
   812             loader.load(mItem.mDocmlFileName, section, &ok);
   710         }
   813         }
   711     }
   814     }
   712     if (ok) {
   815     if (ok) {
   713         // Apply child widget settings.
   816         // Apply child widget settings.
   714         setupNameBasedWidgetProps(loader);
   817         setupNameBasedWidgetProps(loader);
   715         // Find the root view and add it to the mainwindow.
   818         // Find the root view and add it to the mainwindow.
   716         QGraphicsWidget *widget = loader.findWidget(mItem.mDocmlWidgetName);
   819         QGraphicsWidget *widget = loader.findWidget(mItem.mDocmlWidgetName);
   717         if (widget) {
   820         if (widget) {
   718             qDebug() << PRE << "widget created from" << mItem;
   821             splDeb() << PRE << "widget created from" << mItem;
   719             mMainWindow->addView(widget);
   822             mMainWindow->addView(widget);
   720         } else {
   823         } else {
   721             qWarning() << PRE << "widget creation failed from" << mItem;
   824             qWarning() << PRE << "widget creation failed from" << mItem;
   722         }
   825         }
   723     } else {
   826     } else {
   734                 || (req.mOrientation == QLatin1String("landscape") && mItem.mOrientation != Qt::Horizontal)) {
   837                 || (req.mOrientation == QLatin1String("landscape") && mItem.mOrientation != Qt::Horizontal)) {
   735             continue;
   838             continue;
   736         }
   839         }
   737         HbWidget *widget = qobject_cast<HbWidget *>(loader.findWidget(req.mTargetWidgetName));
   840         HbWidget *widget = qobject_cast<HbWidget *>(loader.findWidget(req.mTargetWidgetName));
   738         if (widget) {
   841         if (widget) {
   739             qDebug() << PRE << "setting background item" << req.mFrameGraphicsName
   842             splDeb() << PRE << "setting background item" << req.mFrameGraphicsName
   740                      << "for" << req.mTargetWidgetName;
   843                      << "for" << req.mTargetWidgetName;
   741             widget->setBackgroundItem(
   844             widget->setBackgroundItem(
   742                 new HbFrameItem(req.mFrameGraphicsName, req.mFrameGraphicsType),
   845                 new HbFrameItem(req.mFrameGraphicsName, req.mFrameGraphicsType),
   743                 (int) req.mZValue);
   846                 (int) req.mZValue);
   744         }
   847         }
   745     }
   848     }
   746 }
   849 }
   747 
   850 
   748 void HbSplashGenerator::finishWindow()
   851 void HbSplashGenerator::finishWindow()
   749 {
   852 {
       
   853     QTime prepTime;
       
   854     prepTime.start();
       
   855 
   750     // There must be a view always in order to support view-specific settings.
   856     // There must be a view always in order to support view-specific settings.
   751     if (mMainWindow->views().isEmpty()) {
   857     if (mMainWindow->views().isEmpty()) {
   752         mMainWindow->addView(new HbWidget);
   858         mMainWindow->addView(new HbWidget);
   753     }
   859     }
   754 
   860 
   824         }
   930         }
   825     }
   931     }
   826 
   932 
   827     // Hide dynamic content from status bar (clock, indicators).
   933     // Hide dynamic content from status bar (clock, indicators).
   828     setStatusBarElementsVisible(mMainWindow, false);
   934     setStatusBarElementsVisible(mMainWindow, false);
       
   935 
       
   936     splDeb() << PRE << "time spent in finishWindow() (ms):" << prepTime.elapsed();
       
   937 
       
   938     // Continue with rendering the graphics view in processWindow().
       
   939     NEXT_STAGE(this, processWindow);
   829 }
   940 }
   830 
   941 
   831 void HbSplashGenerator::setStatusBarElementsVisible(HbMainWindow *mw, bool visible)
   942 void HbSplashGenerator::setStatusBarElementsVisible(HbMainWindow *mw, bool visible)
   832 {
   943 {
   833     HbMainWindowPrivate *mwd = HbMainWindowPrivate::d_ptr(mw);
   944     HbMainWindowPrivate *mwd = HbMainWindowPrivate::d_ptr(mw);
   860         QString fullName = dir.filePath(name + '_' + lang);
   971         QString fullName = dir.filePath(name + '_' + lang);
   861         // fullName is not necessarily an existing file, however the translator
   972         // fullName is not necessarily an existing file, however the translator
   862         // may still pick up another suitable file based on this name.
   973         // may still pick up another suitable file based on this name.
   863         if (translator->load(fullName)) {
   974         if (translator->load(fullName)) {
   864             QCoreApplication::installTranslator(translator);
   975             QCoreApplication::installTranslator(translator);
   865             qDebug() << PRE << "translator installed:" << fullName;
   976             splDeb() << PRE << "translator installed:" << fullName;
   866             ok = true;
   977             ok = true;
   867             break;
   978             break;
   868         }
   979         }
   869     }
   980     }
   870     if (ok) {
   981     if (ok) {
   911 {
  1022 {
   912     // This version is used by the compositors. Besides resetting the flag it
  1023     // This version is used by the compositors. Besides resetting the flag it
   913     // also queues a call to processQueue() if needed.
  1024     // also queues a call to processQueue() if needed.
   914     unlockMainWindowInternal();
  1025     unlockMainWindowInternal();
   915     if (mProcessQueuePending) {
  1026     if (mProcessQueuePending) {
   916         QMetaObject::invokeMethod(this, "processQueue", Qt::QueuedConnection);
  1027         NEXT_STAGE(this, processQueue);
   917     }
  1028     }
   918 }
  1029 }
       
  1030 
       
  1031 bool HbSplashGenerator::newRegenPending() const
       
  1032 {
       
  1033     return mQueue.isEmpty() ? false : mQueue.head().mFirstInRegen;
       
  1034 }