BREWアプリを Windows へ移植(6)
メインループについて。
WinMain() からメインスレッド開始までの処理は、YaneGameSDK3rd のサンプルの通りに書きます。
class WinAppFrame : public CAppFrame , public IWinHook{ public: static WinAppFrame* Get(){ return (WinAppFrame*)CAppManager::GetMyFrame(); } CFastDraw* GetDraw() { return GetDrawFactory()->GetDraw(); } private: // override in CAppFrame virtual void MainThread(); // override in IWinHook virtual LRESULT WndProc(HWND,UINT,WPARAM,LPARAM); /// 描画 CFastPlaneFactory* GetDrawFactory() { return &_planeFactory; } CFastPlaneFactory _planeFactory; // ↑こいつがCFastDrawを内包しているので、こいつ経由で描画すれば良い };
void WinAppFrame::MainThread() { GetMyApp()->GetMyWindow()->GetHookList()->Add( this ); GetDraw()->SetDisplay(); // これをメインプリにする(終了するときに、他のウィンドゥをすべて閉じる) SetMainApp(true); while (IsThreadValid()){ // ここにメイン処理書く } GetMyApp()->GetMyWindow()->GetHookList()->Del( this ); } LRESULT WinAppFrame::WndProc( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam ){ // 今のところは何もない return 0; }
// これがmain windowのためのクラス。 class WinAppMain : public CAppBase { // アプリケーションクラスから派生 virtual void MainThread(){ // これがワーカースレッド WinAppFrame().Start(); } }; // 言わずと知れたWinMain int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) { { //* { // エラーログをファイルに出力するのら! CTextOutputStreamFile* p = new CTextOutputStreamFile; p->SetFileName("Error.txt"); Err.SelectDevice(smart_ptr<ITextOutputStream>(p)); } //*/ CAppInitializer init(hInstance,hPrevInstance,lpCmdLine,nCmdShow); // ↑必ず書いてね CSingleApp sapp; if (sapp.IsValid()) { CThreadManager::CreateThread(new WinPort::WinAppMain); // 上で定義したメインのウィンドゥを作成 // CThreadManager::CreateThread(new CAppMainWindow); // ↑複数書くと、複数ウィンドゥが生成されるのだ } // ここでCAppInitializerがスコープアウトするのだが、このときに // すべてのスレッドの終了を待つことになる } return 0; }
ウインドウメッセージを使う可能性があると思うので、IWinHook インターフェースを実装しています。
で、メインアプレットは WinAppFrame の中に書いても良いのですが、メインアプレットそのものを複数作る可能性もあるので、これは別クラスにして、WinAppFrame に追加登録する形で使っていきます。
// WinApplet だと IApplet 継承クラスの名前と紛らわしいので、 // WinApplication って名前にしておく class WinApplication{ public: // アプレットの表示位置 virtual int GetLocationX() const{ return _x; } virtual int GetLocationY() const{ return _y; } virtual void SetLocationX( int x ){ _x = x; } virtual void SetLocationY( int y ){ _y = y; } virtual IApplet* GetApplet(){ return _pIApplet; } // これを呼び出すと、このアプレットは終了する virtual void Close(){ _bClose = true; } virtual bool IsValid() const{ return !_bClose; } // 定期的に呼ばれ続ける関数。ここでタイマやキーなどのコールバック処理を行う virtual void Run(); static WinApplication* CreateApplication( AEECLSID clsId ); static void ReleaseApplication( WinApplication* app ); protected: WinApplication(); virtual ~WinApplication(); int Post(AEEEvent evt, uint16 wp, uint32 dwp); protected: int _x; int _y; WinShell* _pWinShell; IModule* _pIModule; IApplet* _pIApplet; bool _bClose; };
WinApplication* WinApplication::CreateApplication( AEECLSID clsId ){ WinApplication* pApp = new WinApplication(); // シェルクラスを生成 pApp->_pWinShell = new WinShell(); // モジュールを生成 if( ISHELL_CreateInstance( pApp->_pWinShell , clsId , (void**)&pApp->_pIModule ) != SUCCESS ){ ReleaseApplication( pApp ); return null; } // アプレットを生成 if( IMODULE_CreateInstance( pApp->_pIModule , pApp->_pWinShell , clsId , (void**)&pApp->_pIApplet ) != SUCCESS ){ ReleaseApplication( pApp ); return null; } // EVT_APP_START を呼び出す AEEAppStart p; MEMSET( &p , 0 , sizeof( AEEAppStart ) ); p.clsApp = clsId; pApp->Post( EVT_APP_START , 0 , (int32)&p ); return pApp; } void WinApplication::ReleaseApplication( WinApplication* app ){ delete app; } WinApplication::WinApplication() : _bClose( false ) , _x( 0 ) , _y( 0 ){ } WinApplication::~WinApplication(){ Post( EVT_APP_STOP , 0 , 0 ); IMODULE_Release( _pIModule ); ISHELL_Release( _pWinShell ); } void WinApplication::Run(){ // ここでタイマやキーなどのコールバック処理を行う } int WinApplication::Post(AEEEvent evt, uint16 wp, uint32 dwp){ return GetApplet()->pvt->HandleEvent( GetApplet() , evt , wp , dwp ); }
// _activeApplication は、GETAPPINSTANCE() が返す、アクティブになっているアプレットへのポインタ。 // これをセットしておかないと、GETAPPINSTANCE() でどのアプレットを渡せばいいか分からなくなる。 while (IsThreadValid()){ // 終了しているアプレットを削除する { ApplicationList::iterator it = _applicationList.begin(); while( it != _applicationList.end() ){ _activeApplication = (*it); if( !(*it)->IsValid() ){ it = _applicationList.erase( it ); }else{ ++it; } } } // 全てのアプレットが終了していたら、メインループを抜けてアプリケーションを終了する。 if( _applicationList.empty() ){ break; } // WinApplication::Run() を実行する。 for( ApplicationList::iterator it = _applicationList.begin() ; it != _applicationList.end() ; ++it ){ _activeApplication = (*it); (*it)->Run(); } }
これで一応、複数のアプレットを生成してメインループの処理が出来るようになります。