/******************************************************************* * * File: App.cpp * * Author: Peter van Sebille (peter@yipton.net) * * Modified/adapted for picodriveN by notaz, 2006 * * (c) Copyright 2006, notaz * (c) Copyright 2002, Peter van Sebille * All Rights Reserved * *******************************************************************/ #include "app.h" // #include "picodriven.mbg" // bitmap identifiers #include "picodriven.rsg" #include #include //#include //#include #include // CEikMenuBar #include // TApaSystemEvent #include "Dialogs.h" CApaDocument* CPicolApplication::CreateDocumentL() { return new (ELeave) CPicolDocument(*this); } CPicolDocument::CPicolDocument(CEikApplication& aApp) : CEikDocument(aApp) { } CPicolDocument::~CPicolDocument() { } CEikAppUi* CPicolDocument::CreateAppUiL() { return new(ELeave) CPicolAppUi; } //////////////////////////////////////////////////////////////// // // class CPicolAppUi // //////////////////////////////////////////////////////////////// CPicolAppUi::CPicolAppUi() : iCurrentLConfig(iCurrentConfig) { // set default config Mem::FillZ(&iCurrentConfig, sizeof(iCurrentConfig)); iCurrentConfig.iFlags = 1; // use_sram iCurrentConfig.iFrameskip = TPicoConfig::PFSkipAuto; iCurrentConfig.iScreenRotation = TPicoConfig::PRot90; } CPicolAppUi::~CPicolAppUi() { delete iAppView; DeregisterView(*iFOView); delete iFOView; DeregisterView(*iFCView); delete iFCView; } void CPicolAppUi::ConstructL() { BaseConstructL(); // load config iCurrentLConfig.Load(); iAppView=new(ELeave) CEPicolAppView; iAppView->ConstructL(ClientRect()); iFOView=new(ELeave) CPicolFOView(*iAppView); RegisterViewL(*iFOView); iFCView=new(ELeave) CPicolFCView(*iAppView); RegisterViewL(*iFCView); } void CPicolAppUi::HandleCommandL(TInt aCommand) { TInt oldFrameskip = iCurrentConfig.iFrameskip; TInt res; // give time for config dialog destruction if(iAfterConfigDialog) { iAfterConfigDialog = EFalse; TTime now; now.UniversalTime(); if(now.MicroSecondsFrom(iConfigDialogClosed).Int64().Low() < 2500*1000) User::After(2500*1000-now.MicroSecondsFrom(iConfigDialogClosed).Int64().Low()); } switch (aCommand) { case EEikCmdPicoLoadState: if(iGameRunner) { CEikonEnv::Static()->BusyMsgL(_L("Loading State")); res = ss.SendReceive(PicoMsgLoadState, 0); CEikonEnv::Static()->BusyMsgCancel(); // emu doesn't start to run if load fails, so we can display this if(res) CEikonEnv::Static()->InfoMsg(_L("Load Failed")); } break; case EEikCmdPicoSaveState: if(iGameRunner) { CEikonEnv::Static()->BusyMsgL(_L("Saving State")); res = ss.SendReceive(PicoMsgSaveState, 0); CEikonEnv::Static()->BusyMsgCancel(); if(res) CEikonEnv::Static()->InfoMsg(_L("Save Failed")); } break; case EEikCmdPicoLoadROM: DisplayOpenROMDialogL(); break; case EEikCmdPicoResume: ss.Send(PicoMsgResume, 0); iEmuRunning = ETrue; break; case EEikCmdPicoReset: ss.Send(PicoMsgReset, 0); iEmuRunning = ETrue; break; case EEikCmdPicoKeys: if(!iGameRunner) RunGameL(); ss.Send(PicoMsgKeys, 0); iEmuRunning = ETrue; break; case EEikCmdPicoSettings: DisplayConfigDialogL(); break; case EEikCmdHelpAbout: // EEikCmdPicoAbout: DisplayAboutDialogL(); break; // standard identifier must be used here, TApaTask::EndTask() and probably others send it case EEikCmdExit: // EEikCmdPicoExit: if(iGameRunner) { iQuitting = ETrue; iExitForcer = CExitForcer::NewL(*this, 2000); ss.Send(PicoMsgQuit, 0); } else { iCurrentLConfig.Save(); DEBUGPRINT(_L("[app] Exit (menu)")); Exit(); } break; // frameskips case EEikCmdPicoFrameskipAuto: iCurrentConfig.iFrameskip = TPicoConfig::PFSkipAuto; break; case EEikCmdPicoFrameskip0: iCurrentConfig.iFrameskip = TPicoConfig::PFSkip0; break; case EEikCmdPicoFrameskip1: iCurrentConfig.iFrameskip = 1; break; case EEikCmdPicoFrameskip2: iCurrentConfig.iFrameskip = 2; break; case EEikCmdPicoFrameskip4: iCurrentConfig.iFrameskip = 4; break; case EEikCmdPicoFrameskip8: iCurrentConfig.iFrameskip = 8; break; case EEikCmdPicoDebugKillEmu: if(iGameRunner) { iExitForcer = CExitForcer::NewL(*this, 4000); ss.Send(PicoMsgQuit, 0); } break; case EEikCmdPicoDebugInfo: if(iGameRunner) DisplayDebugDialogL(); break; } // send config update if needed if(iCurrentConfig.iFrameskip != oldFrameskip) SendConfig(); } void CPicolAppUi::HandleSystemEventL(const TWsEvent& aEvent) { TApaSystemEvent event; event = *(TApaSystemEvent*)aEvent.EventData(); if(event == EApaSystemEventBroughtToForeground) // application brought to foreground { DEBUGPRINT(_L("[app] EApaSystemEventBroughtToForeground, iEmuRunning=%i"), iEmuRunning); // we might have missed flip open event (when moved to background), // so make sure we have correct view active if(iCoeEnv->ScreenDevice()->CurrentScreenMode() == EScreenModeFlipOpen) { ActivateViewL(TVwsViewId(KUidPicolApp, KUidPicolFOView)); return; } } if(event == EApaSystemEventShutdown) { DEBUGPRINT(_L("[app] EApaSystemEventShutdown")); } CEikAppUi::HandleSystemEventL(aEvent); } // called just before the menu is shown void CPicolAppUi::DynInitMenuPaneL(TInt aResourceId, CEikMenuPane* aMenuPane) { if(aResourceId == R_APP_EMU_MENU) { TBool dimmed = !iGameRunner || !iROMLoaded; aMenuPane->SetItemDimmed(EEikCmdPicoLoadState, dimmed); aMenuPane->SetItemDimmed(EEikCmdPicoSaveState, dimmed); aMenuPane->SetItemDimmed(EEikCmdPicoResume, dimmed); aMenuPane->SetItemDimmed(EEikCmdPicoReset, dimmed); } else if(aResourceId == R_APP_FRAMESKIP_MENU) { TInt itemToCheck = EEikCmdPicoFrameskipAuto; switch(iCurrentConfig.iFrameskip) { case 0: itemToCheck = EEikCmdPicoFrameskip0; break; case 1: itemToCheck = EEikCmdPicoFrameskip1; break; case 2: itemToCheck = EEikCmdPicoFrameskip2; break; case 4: itemToCheck = EEikCmdPicoFrameskip4; break; case 8: itemToCheck = EEikCmdPicoFrameskip8; break; } aMenuPane->SetItemButtonState(itemToCheck, EEikMenuItemSymbolOn); } } void CPicolAppUi::DisplayAboutDialogL() { CEikDialog* dialog = new(ELeave) CAboutDialog; TInt iButtonRes = dialog->ExecuteLD(R_DIALOG_ABOUT); if(iButtonRes == EEikBidYes) { CCreditsDialog *creditsDialog = new (ELeave) CCreditsDialog(); creditsDialog->iMessageResourceID = R_TBUF_CREDITS; creditsDialog->ExecuteLD(R_DIALOG_CREDITS); } } void CPicolAppUi::DisplayOpenROMDialogL() { TFileName file(iCurrentLConfig.iLastROMFile); CEikDialog* dialog = new(ELeave) CEikFileOpenDialog(&file); //((CEikFileOpenDialog *)dialog)->SetRequiredExtension(&ext); if(dialog->ExecuteLD(R_EIK_DIALOG_FILE_OPEN) == EEikBidOk) { CEikonEnv::Static()->BusyMsgL(_L("Loading ROM")); // start emu process if it is not running if(!iGameRunner) RunGameL(); iROMLoaded = EFalse; TBuf8 file8; file8.Copy(file); TAny *p[KMaxMessageArguments]; p[0]= (TAny*)(&file8); TInt res = ss.SendReceive(PicoMsgLoadROM, &p[0]); CEikonEnv::Static()->BusyMsgCancel(); if(res == 1) CEikonEnv::Static()->InfoWinL(_L("Error"), _L("Failed to open file.")); else if(res == 2) CEikonEnv::Static()->InfoWinL(_L("Error"), _L("Failed to allocate memory.")); else if(res == 3) CEikonEnv::Static()->InfoWinL(_L("Error"), _L("The file you selected is not a game ROM.")); else if(res == 4) CEikonEnv::Static()->InfoWinL(_L("Error"), _L("No game ROMs found in zipfile.")); else if(res == 5) CEikonEnv::Static()->InfoWinL(_L("Error"), _L("Failed while unzipping ROM.")); else if(res < 0) CEikonEnv::Static()->InfoWinL(_L("Error"), _L("Failed to send request to emu process.")); else { iROMLoaded = ETrue; iEmuRunning = ETrue; } // sound errors which leave ROM loaded if(res == 6) CEikonEnv::Static()->InfoWinL(_L("Error"), _L("Failed to allocate sound buffer, disabled sound.")); else if(res == 7) CEikonEnv::Static()->InfoWinL(_L("Error"), _L("Failed to start soundsystem, disabled sound.")); if(res == 6 || res == 7) iCurrentConfig.iFlags &= ~4; iCurrentLConfig.iLastROMFile.Copy(file); } } void CPicolAppUi::DisplayConfigDialogL() { CPicoConfigDialog* configDialog = new(ELeave)CPicoConfigDialog(iCurrentConfig, iCurrentLConfig); configDialog->ExecuteLD(R_PICO_CONFIG); if(iGameRunner) SendConfig(); iCurrentLConfig.Save(); // configDialog seems to be actually destroyed later after returning, // and this usually happens just after resuming game and causes emu slowdowns :/ iAfterConfigDialog = ETrue; iConfigDialogClosed.UniversalTime(); } void CPicolAppUi::DisplayDebugDialogL() { // first get our debug info char dtxt[1024]; TAny *p[KMaxMessageArguments]; TPtr8 descr((TUint8*) dtxt, sizeof(dtxt)); p[0]= (TAny*)(&descr); ss.SendReceive(PicoMsgRetrieveDebugStr, &p[0]); CEikDialog* dialog = new(ELeave) CDebugDialog(dtxt); dialog->ExecuteLD(R_DIALOG_DEBUG); } void CPicolAppUi::SendConfig() { // send config if(iGameRunner) { TAny *p[KMaxMessageArguments]; TPtrC8 descr((TUint8*) &iCurrentConfig, sizeof(iCurrentConfig)); p[0]= (TAny*)(&descr); ss.Send(PicoMsgConfigChange, &p[0]); } } // get config from emu proc void CPicolAppUi::RetrieveConfig() { // ask to configure keys and receive new config TAny *p[KMaxMessageArguments]; TPtr8 descr((TUint8*) &iCurrentConfig, sizeof(iCurrentConfig)); p[0]= (TAny*)(&descr); ss.SendReceive(PicoMsgRetrieveConfig, &p[0]); iCurrentLConfig.Save(); } void CPicolAppUi::NotifyEmuDeath() { StopGame(); if(iQuitting) { DEBUGPRINT(_L("[app] Exit (NotifyEmuDeath)")); iCurrentLConfig.Save(); RProcess me; me.Terminate(0); } } void CPicolAppUi::NotifyForcedExit() { DEBUGPRINT(_L("[app] Exit (NotifyForcedExit)")); StopGame(); RProcess me; me.Terminate(0); } TBool CPicolAppUi::EmuRunning() const { return iEmuRunning; } void CPicolAppUi::StopGame() { // in case we have busyMsg and process crashes CEikonEnv::Static()->BusyMsgCancel(); ss.Close(); if(iGameRunner) delete iGameRunner; iGameRunner = NULL; if(iExitForcer) delete iExitForcer; iExitForcer = NULL; if(iThreadWatcher1) delete iThreadWatcher1; iThreadWatcher1 = NULL; iROMLoaded = EFalse; iEmuRunning = EFalse; } void CPicolAppUi::RunGameL() { TInt res = KErrNone; // do one connection attemt to emu and ask it to quit if we succeed res = ss.Connect(); if(res == KErrNone) { ss.Send(PicoMsgQuit, 0); ss.Close(); } iGameRunner = CGameRunner::NewL(*this); // Connect to the server // we don't want to do complex asynchronous stuff here, so we just // wait and do several connection attempts User::After(200000); for(TInt attempt=0; attempt < 10; attempt++) { res = ss.Connect(); if(res == KErrNone) break; User::After(200000); } if(res != KErrNone) { CEikonEnv::Static()->BusyMsgCancel(); CEikonEnv::Static()->InfoWinL(_L("Error"), _L("Failed to communicate with the emulation process.")); StopGame(); RProcess me; me.Terminate(2); } // now we are successfully connected, that means emu process' helper-communication thread is running. // we have to keep an eye on it too, because if it crashes, symbian OS leaves it's process // alive, but it becomes useless without it's communication thread so we have to detect it's death. iThreadWatcher1 = CThreadWatcher::NewL(*this, KServerName); // send initial config SendConfig(); } /* void CPicolAppUi::HandleScreenDeviceChangedL() { // does not receive when emu is in foreground if(iCoeEnv->ScreenDevice()->CurrentScreenMode() == 0) { // flip open // regain focus //iCoeEnv->BringOwnerToFront(); } // ss.Send(PicoMsgFlipChange, 0); } */ void CPicolAppUi::HandleApplicationSpecificEventL(TInt aType, const TWsEvent& aEvent) { DEBUGPRINT(_L("[app] event from server: %i"), aEvent.Type()); switch (aEvent.Type()) { case EEventKeyCfgDone: RetrieveConfig(); break; case EEventGamePaused: iEmuRunning = EFalse; break; } } //////////////////////////////////////////////////////////////// // // class CEPicolAppView // //////////////////////////////////////////////////////////////// void CEPicolAppView::ConstructL(const TRect& aRect) { CreateWindowL(); SetRect(aRect); ActivateL(); /* * Load background image */ /* TBuf<1> name = _L("*"); TRAPD(err, iBgImage = CEikonEnv::Static()->CreateBitmapL(name, EMbmEdoomDoom)); if (iBgImage) { iImagePosition.iX = (aRect.Size().iWidth - iBgImage->SizeInPixels().iWidth) / 2; iImagePosition.iY = (aRect.Size().iHeight - iBgImage->SizeInPixels().iHeight) / 2; } */ } CEPicolAppView::~CEPicolAppView() { //if (iBgImage) delete iBgImage; } void CEPicolAppView::Draw(const TRect& aRect) const { CWindowGc& gc = SystemGc(); //if (iBgImage) //{ // gc.DrawBitmap(iImagePosition, iBgImage); // DrawUtils::ClearBetweenRects(gc, Rect(), TRect(iImagePosition, iBgImage->SizeInPixels())); //} //else gc.Clear();//aRect); } //////////////////////////////////////////////////////////////// // // class CPicolViewBase // //////////////////////////////////////////////////////////////// void CPicolViewBase::ViewActivatedL(const TVwsViewId& /*aPrevViewId*/, TUid /*aCustomMessageId*/, const TDesC8& /*aCustomMessage*/) { TPixelsAndRotation sizeAndRotation; CEikonEnv::Static()->ScreenDevice()->GetDefaultScreenSizeAndRotation(sizeAndRotation); CEikonEnv::Static()->ScreenDevice()->SetScreenSizeAndRotation(sizeAndRotation); //iAppViewCtl.MakeVisible(ETrue); } void CPicolViewBase::ViewDeactivated() { //iAppViewCtl.MakeVisible(EFalse); } //////////////////////////////////////////////////////////////// // // class CPicolFOView // //////////////////////////////////////////////////////////////// TVwsViewId CPicolFOView::ViewId() const { return TVwsViewId(KUidPicolApp, KUidPicolFOView); } TVwsViewIdAndMessage CPicolFOView::ViewScreenDeviceChangedL() { // only handle change to FC mode when emu process is running if(static_cast(CEikonEnv::Static()->AppUi())->EmuRunning()) return TVwsViewIdAndMessage(TVwsViewId(KUidPicolApp, KUidPicolFCView)); else return MCoeView::ViewScreenDeviceChangedL(); } TBool CPicolFOView::ViewScreenModeCompatible(TInt aScreenMode) { return (aScreenMode == EScreenModeFlipOpen); } void CPicolFOView::ViewActivatedL(const TVwsViewId& aPrevViewId, TUid aCustomMessageId, const TDesC8& aCustomMessage) { DEBUGPRINT(_L("[app] FO")); CPicolViewBase::ViewActivatedL(aPrevViewId, aCustomMessageId, aCustomMessage); CEikonEnv::Static()->AppUiFactory()->MenuBar()->MakeVisible(ETrue); iAppViewCtl.SetRect(static_cast(CEikonEnv::Static()->AppUi())->ClientRect()); } //////////////////////////////////////////////////////////////// // // class CPicolFCView // //////////////////////////////////////////////////////////////// TVwsViewId CPicolFCView::ViewId() const { return TVwsViewId(KUidPicolApp, KUidPicolFCView); } TVwsViewIdAndMessage CPicolFCView::ViewScreenDeviceChangedL() { return TVwsViewIdAndMessage(TVwsViewId(KUidPicolApp, KUidPicolFOView)); } TBool CPicolFCView::ViewScreenModeCompatible(TInt aScreenMode) { return (aScreenMode == EScreenModeFlipClosed); } void CPicolFCView::ViewActivatedL(const TVwsViewId& aPrevViewId, TUid aCustomMessageId, const TDesC8& aCustomMessage) { DEBUGPRINT(_L("[app] FC")); CPicolViewBase::ViewActivatedL(aPrevViewId, aCustomMessageId, aCustomMessage); CEikonEnv::Static()->AppUiFactory()->MenuBar()->MakeVisible(EFalse); //iAppViewCtl.ChangeLayout(ETrue); iAppViewCtl.SetRect(CEikonEnv::Static()->ScreenDevice()->SizeInPixels()); } //////////////////////////////////////////////////////////////// // // framework // //////////////////////////////////////////////////////////////// GLDEF_C TInt E32Dll(TDllReason) { return KErrNone; } EXPORT_C CApaApplication* NewApplication() { return new CPicolApplication; } TUid CPicolApplication::AppDllUid() const { return KUidPicolApp; }