More improvements in Splash Screen (application initialization) code. It's now completely event-driven.

This commit is contained in:
LoRd_MuldeR 2013-10-27 16:13:14 +01:00
parent 758906b4c8
commit 3b0577c733
4 changed files with 117 additions and 72 deletions

Binary file not shown.

View File

@ -35,7 +35,7 @@
#define VER_LAMEXP_MINOR_LO 9
#define VER_LAMEXP_TYPE Alpha
#define VER_LAMEXP_PATCH 4
#define VER_LAMEXP_BUILD 1427
#define VER_LAMEXP_BUILD 1430
#define VER_LAMEXP_CONFG 1348
///////////////////////////////////////////////////////////////////////////////

View File

@ -34,41 +34,46 @@
#define FADE_DELAY 16
#define OPACITY_DELTA 0.02
/* It can happen that the QThread has just terminated and already emitted the 'terminated' signal, but did NOT change the 'isRunning' flag to FALSE yet. */
/* For this reason the macro will first check the 'isRunning' flag. If (and only if) the flag still returns TRUE, we will call the wait() on the thread. */
#define THREAD_RUNNING(THRD) (((THRD)->isRunning()) ? (!((THRD)->wait(1))) : false)
#define SET_TASKBAR_STATE(FLAG) do \
//Setup taskbar indicator
#define SET_TASKBAR_STATE(WIDGET,VAR,FLAG) do \
{ \
if(FLAG) \
{ \
if(!bTaskBar) bTaskBar = WinSevenTaskbar::setTaskbarState(splashScreen, WinSevenTaskbar::WinSevenTaskbarIndeterminateState); \
if(!(VAR)) (VAR) = WinSevenTaskbar::setTaskbarState((WIDGET), WinSevenTaskbar::WinSevenTaskbarIndeterminateState); \
} \
else \
{ \
if(bTaskBar) bTaskBar = (!WinSevenTaskbar::setTaskbarState(splashScreen, WinSevenTaskbar::WinSevenTaskbarNoState)); \
if((VAR)) (VAR) = (!WinSevenTaskbar::setTaskbarState((WIDGET), WinSevenTaskbar::WinSevenTaskbarNoState)); \
} \
} \
while(0)
#define ASYNC_WAIT(LOOP, DELAY) do \
{ \
QTimer::singleShot((DELAY), (LOOP), SLOT(quit())); \
(LOOP)->exec(QEventLoop::ExcludeUserInputEvents); \
} \
while(0)
////////////////////////////////////////////////////////////
// Constructor
////////////////////////////////////////////////////////////
SplashScreen::SplashScreen(QWidget *parent)
:
m_opacitySteps(qRound(1.0 / OPACITY_DELTA)),
QFrame(parent, Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint)
{
//Init the dialog, from the .ui file
setupUi(this);
//Make size fixed
setFixedSize(this->size());
//Create event loop
m_loop = new QEventLoop(this);
//Create timer
m_timer = new QTimer(this);
m_timer->setInterval(FADE_DELAY);
m_timer->setSingleShot(false);
//Connect timer to slot
connect(m_timer, SIGNAL(timeout()), this, SLOT(updateHandler()));
//Start animation
m_working = new QMovie(":/images/Loading.gif");
labelLoading->setMovie(m_working);
@ -77,8 +82,11 @@ SplashScreen::SplashScreen(QWidget *parent)
//Set wait cursor
setCursor(Qt::WaitCursor);
//Prevent close
//Init status
m_canClose = false;
m_status = STATUS_FADE_IN;
m_fadeValue = 0;
m_taskBarInit = false;
}
////////////////////////////////////////////////////////////
@ -90,9 +98,11 @@ SplashScreen::~SplashScreen(void)
if(m_working)
{
m_working->stop();
delete m_working;
m_working = NULL;
}
LAMEXP_DELETE(m_working);
LAMEXP_DELETE(m_loop);
LAMEXP_DELETE(m_timer);
}
////////////////////////////////////////////////////////////
@ -101,15 +111,10 @@ SplashScreen::~SplashScreen(void)
void SplashScreen::showSplash(QThread *thread)
{
const int opacitySteps = qRound(1.0 / OPACITY_DELTA);
bool bTaskBar = false;
unsigned int deadlockCounter = 0;
SplashScreen *splashScreen = new SplashScreen();
//Show splash
splashScreen->m_canClose = false;
splashScreen->setWindowOpacity(OPACITY_DELTA);
splashScreen->setFixedSize(splashScreen->size());
splashScreen->show();
//Wait for window to show
@ -117,67 +122,85 @@ void SplashScreen::showSplash(QThread *thread)
splashScreen->repaint();
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
//Setup the event loop
QEventLoop *loop = new QEventLoop(splashScreen);
connect(thread, SIGNAL(terminated()), loop, SLOT(quit()), Qt::QueuedConnection);
connect(thread, SIGNAL(finished()), loop, SLOT(quit()), Qt::QueuedConnection);
//Create timer
QTimer *timer = new QTimer();
connect(timer, SIGNAL(timeout()), loop, SLOT(quit()));
//Start the thread
thread->start();
//Connect thread signals
connect(thread, SIGNAL(terminated()), splashScreen, SLOT(threadComplete()), Qt::QueuedConnection);
connect(thread, SIGNAL(finished()), splashScreen, SLOT(threadComplete()), Qt::QueuedConnection);
//Init taskbar
SET_TASKBAR_STATE(true);
SET_TASKBAR_STATE(splashScreen, splashScreen->m_taskBarInit, true);
//Fade in
for(int i = 1; i <= opacitySteps; i++)
//Start the thread
splashScreen->m_timer->start(FADE_DELAY);
QTimer::singleShot(3*60*1000, splashScreen->m_loop, SLOT(quit()));
QTimer::singleShot(0, thread, SLOT(start()));
//Start event handling!
const int ret = splashScreen->m_loop->exec(QEventLoop::ExcludeUserInputEvents);
//Check for timeout
if(ret != 42)
{
const double opacity = (i < opacitySteps) ? (OPACITY_DELTA * static_cast<double>(i)) : 1.0;
splashScreen->setWindowOpacity(opacity); //splashScreen->update();
ASYNC_WAIT(loop, FADE_DELAY);
SET_TASKBAR_STATE(true);
}
//Start the timer
timer->start(30720);
//Loop while thread is still running
while(THREAD_RUNNING(thread))
{
if((++deadlockCounter) > 60)
{
qFatal("Deadlock in initialization thread detected!");
}
ASYNC_WAIT(loop, 5000);
}
//Stop the timer
timer->stop();
//Fade out
for(int i = opacitySteps; i >= 0; i--)
{
const double opacity = OPACITY_DELTA * static_cast<double>(i);
splashScreen->setWindowOpacity(opacity); //splashScreen->update();
ASYNC_WAIT(loop, FADE_DELAY);
thread->terminate();
qFatal("Deadlock in initialization thread encountered!");
}
//Restore taskbar
SET_TASKBAR_STATE(false);
SET_TASKBAR_STATE(splashScreen, splashScreen->m_taskBarInit, false);
//Hide splash
splashScreen->m_canClose = true;
splashScreen->close();
//Free
LAMEXP_DELETE(loop);
LAMEXP_DELETE(timer);
LAMEXP_DELETE(splashScreen);
}
////////////////////////////////////////////////////////////
// SLOTS
////////////////////////////////////////////////////////////
void SplashScreen::updateHandler(void)
{
if(m_status == STATUS_FADE_IN)
{
if(m_fadeValue < m_opacitySteps)
{
setWindowOpacity(OPACITY_DELTA * static_cast<double>(++m_fadeValue));
SET_TASKBAR_STATE(this, m_taskBarInit, true);
}
else
{
setWindowOpacity(1.0);
m_timer->stop();
m_status = STATUS_WAIT;
}
}
else if(m_status == STATUS_FADE_OUT)
{
if(m_fadeValue > 0)
{
setWindowOpacity(OPACITY_DELTA * static_cast<double>(--m_fadeValue));
SET_TASKBAR_STATE(this, m_taskBarInit, true);
}
else
{
setWindowOpacity(0.0);
m_timer->stop();
m_status = STATUS_DONE;
m_loop->exit(42);
}
}
}
void SplashScreen::threadComplete(void)
{
m_status = STATUS_FADE_OUT;
if(!m_timer->isActive())
{
m_timer->start(FADE_DELAY);
}
}
////////////////////////////////////////////////////////////
// EVENTS
////////////////////////////////////////////////////////////

View File

@ -33,14 +33,36 @@ class SplashScreen: public QFrame, private Ui::SplashScreen
Q_OBJECT
public:
SplashScreen(QWidget *parent = 0);
~SplashScreen(void);
static void showSplash(QThread *thread);
private:
SplashScreen(QWidget *parent = 0);
~SplashScreen(void);
enum
{
STATUS_FADE_IN = 0,
STATUS_WAIT = 1,
STATUS_FADE_OUT = 2,
STATUS_DONE = 3
}
status_t;
QMovie *m_working;
bool m_canClose;
QEventLoop *m_loop;
QTimer *m_timer;
const unsigned int m_opacitySteps;
unsigned int m_status;
unsigned int m_fadeValue;
volatile bool m_canClose;
volatile bool m_taskBarInit;
private slots:
void updateHandler(void);
void threadComplete(void);
protected:
void keyPressEvent(QKeyEvent *event);