游戏软件一般用CDirectScreenAccess进行直接写屏。大家都知道,WindowServer会在需要停止直接写屏时回调MDirectScreenAccess::AbortNow接口函数,在可以重新启动时回调MDirectScreenAccess::Restart接口函数。可是具体在这两个函数中做什么,SDK没有过多的介绍。我在此说一下我的做法。如果你合理的处理了这两个函数,就可以轻松应对来电、屏保、程序切换等事件。
我们先说AbortNow,它的处理比较简单。你之需在其中停止驱动游戏逻辑的计时器(一般是个CPeriodic对象),停止声音模块(一般是一个CActive任务)就可以了。
值得费些力气的是Restart函数,它并不是在应用程序回到前台,并且可以进行全屏直接写屏时才被回调。所以不能在此时武断的恢复游戏逻辑,开始游戏。
首先,你要调用CDirectScreenAccess::StartL( )恢复直接写屏。但是必须给这个函数加上TRAP保护。因为它很可能抛出KErrNotReady异常。如果遇到这个异常,那你就直接返回好了,因为直接写屏此时并不能开始。接下来你需要检查一下绘图区域,看是否整个屏幕都可以被使用。如果不是,那也无需启动游戏逻辑,只需要用最后保留的后台缓冲区的内容更新直接写屏区域即可。第三种情况,如果直接写屏成功启动,并且整个屏幕都可以被绘制,才启动游戏逻辑,启动声音等其它模块。
完整的代码如下:
void CEngine::AbortNow(RDirectScreenAccess::TTerminationReasons /*aReason*/)
{
// Cancel timer and display
if (iGameTimer->IsActive())
iGameTimer->CancelTimer();
if (!iGameWorldPaused)
{
iGameWorldPaused = ETrue;
iGameWorld->PauseGame(); // Pause audio stream etc.
}
iPaused = ETrue;
}
void CEngine::Restart(RDirectScreenAccess::TTerminationReasons /*aReason*/)
{
TRAPD(error, SetupDirectScreenAccessL());
switch(error)
{
case KErrNone:
break;
case KErrNotReady:
if (iDirectScreenAccess->IsActive())
iDirectScreenAccess->Cancel();
if (iGameTimer->IsActive())
iGameTimer->CancelTimer();
if (!iGameWorldPaused)
{
iGameWorldPaused = ETrue;
iGameWorld->PauseGame();
}
return;
default:
User::Panic(_L("Setup DSA Error"), error);
}
if(iPaused)
{
if(iGameDawingArea == iRegion->BoundingRect())
{
iPaused = EFalse;
if(!iGameTimer->IsActive())
{
iGameWorldPaused = EFalse;
iGameWorld->ResumeGame();
iGameTimer->Restart();
}
}
else
{
PauseFrame();
}
}
else
{
if(!iGameTimer->IsActive())
{
iGameTimer->Restart();
}
}
}
void CEngine::SetupDirectScreenAccessL()
{
// Initialise DSA
iDirectScreenAccess->StartL();
// Get graphics context for it
iGc = iDirectScreenAccess->Gc();
// Get region that DSA can draw in
iRegion = iDirectScreenAccess->DrawingRegion();
// Set the display to clip to this region
iGc->SetClippingRegion(iRegion);
}
void CEngine::PauseFrame()
{
// Force screen update: this is required for WINS, but may
// not be for all hardware:
iDirectScreenAccess->ScreenDevice()->Update();
// and draw from unchanged offscreen bitmap
iGc->BitBlt(TPoint(0,0), &(iDoubleBufferedArea->GetDoubleBufferedAreaBitmap()));
iClient.Flush();
}
};