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();
        }
    }

これで一応、複数のアプレットを生成してメインループの処理が出来るようになります。