Microsoft DirectX 8.0 (C++)

ステップ 6 : マウスからのバッファリング データの取得

マウスを取得すれば、アプリケーションは、マウスからのデータの取得を開始することができる。

Scrawl サンプルでは、データ取得は、通知されたイベントがトリガとなる。シグナルやメッセージがあることが MsgWaitForMultipleObjects から示されるまで、アプリケーションは、WinMain 関数の中でスリープ状態に入る。マウス関連のシグナルがあれば、OnMouseInput 関数が呼び出される。この関数は、次のプロセスで示すように、バッファリング データ入力の処理方法を示すよい例である。

まず、この関数は、元のカーソル位置が完全に消去されていることを確認する。Scrawl は独自のカーソルを維持し、その描画や消去に対して全面的に責任を持つ。

VOID OnMouseInput(HWND hWnd)
{
    /* 元のカーソルを消去するため、無効にする。*/
    InvalidateCursorRect(hWnd);
 

ここで、関数はループに入り、バッファの全内容に対する読み取りおよび応答を行う。データは一度に 1 項目しか取得できないので、データを保持するためには、DIDEVICEOBJECTDATA 構造体が 1 つだけあればよい。

    while (!bDone) {
        DIDEVICEOBJECTDATA od;
        DWORD dwElements = 1;   // 取得すべき項目数
 

  入力処理にはこのほかにも、バッファ全体を一度に読み取った後、ループで取得項目を順に 1 つずつ応答していく方法がある。その場合は、dwElements をバッファ サイズとし、od はそのサイズに等しい数の要素を持つ配列とする。

アプリケーションは、データを取得するために IDirectInputDevice8::GetDeviceData メソッドを呼び出す。第 2 パラメータはデータを置く位置を、第 3 パラメータは必要な項目数を、Microsoft® DirectInput® にそれぞれ通知する。データをバッファに残しておく場合は、最後のパラメータを DIGDD_PEEK とする。ただし、この場合は、データを再度必要とすることはないので削除する。

        hr = g_pMouse->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), 
                                     &od, &dwElements, 0);
 

ここで、アプリケーションは、デバイスに対するアクセス権が失われているかどうかの確認チェックを行い、失われていればすぐに最初のチャンスでマウスの再取得を行おうとする。この手順は、「ステップ 5 : マウスへのアクセス権の管理」で説明する。

        if (hr == DIERR_INPUTLOST) 
        {
            SetAcquire();
            break;
        }
 

次に、アプリケーションは、GetDeviceData メソッドの呼び出しが成功したこと、および取得するデータが実際に存在したことを確認する。GetDeviceData の呼び出し後、変数 dwElements は実際に取得した項目数を示していることに注意する。

        /* データが読めない、または利用可能データがない。*/
        if (FAILED(hr) || dwElements == 0) 
        {
            break;
        }
 

ここまでのプロセスが終了した場合、呼び出しは成功し、バッファにはデータの項目が入っている。ここで、アプリケーションは、DIDEVICEOBJECTDATA 構造体の dwOfs メンバを調べ、デバイス上のどのオブジェクトが状態変化を報告したかを確認する。次に、ヘルパー関数を呼び出し、適切に対応する。これらの関数には dwData メンバが渡される。このメンバはデバイス オブジェクトに何が発生したかを格納する。

        /* 要素を調べて何が発生したかを確認する。*/
 
        switch (od.dwOfs) 
        {
            // マウスの水平動作。
            case DIMOFS_X: 
                UpdateCursorPosition(od.dwData, 0); 
                break;
 
            // マウスの垂直動作。
            case DIMOFS_Y: 
                UpdateCursorPosition(0, od.dwData); 
                break; 
 
            // DIMOFS_BUTTON0:右ボタンが押された、または離された。 
            case DIMOFS_BUTTON0:
            // DIMOFS_BUTTON1:左ボタンが押された、または離された。
            case DIMOFS_BUTTON1:
                // 右ボタンまたはスワップされた左ボタンが押し下げられているか。
                if((g_bSwapMouseButtons && 
                               DIMOFS_BUTTON1 == od.dwOfs) ||
                   (!g_bSwapMouseButtons && 
                               DIMOFS_BUTTON0 == od.dwOfs))
                {
                    if (od.dwData & 0x80)  // 左ボタンが押されているため、
                                           // ボタン押下モードになる。
                    {
                        bDone = TRUE;
                        OnLeftButtonDown(hWnd);
                    }
                // 左ボタンまたはスワップされた右ボタンが押し下げられているか。
                if((g_bSwapMouseButtons && 
                               DIMOFS_BUTTON0 == od.dwOfs) ||
                   (!g_bSwapMouseButtons && 
                               DIMOFS_BUTTON1 == od.dwOfs))
                {
                    if(!(od.dwData & 0x80))  // ボタンが離されているため、
                                             // ショートカット メニューをチェックする。
                    {  
                        bDone = TRUE;
                        OnRightButtonUp(hWnd); 
                    }
                }
                break;        
        }
 

最後に、ヘルパー関数の 1 つによってカーソルが移動された場合、OnMouseInput サンプル関数がカーソルが占有している画面の四角形を無効にする。

    // 新しいカーソルが描画されるので無効にする。
    InvalidateCursorRect(hWnd);
}
 

また、Scrawl は、OnLeftButtonDown 関数にあるマウス データを集める。これは、主ボタンが押下されている間、つまり、ユーザーが描画している間のマウス移動を、アプリケーションが追跡する関数である。この関数は、イベント通知には依存しないが、ボタンが離されるまで、連続的に DirectInput バッファをポーリングする。

OnLeftButtonDown 関数では、待機中のデータがすべて読み込まれるまで描画は行われないことに注意する。これは、マウスの水平方向と垂直方向の移動が、独立したイベントとして報告されるためである (ただし、両イベントのバッファへの配置は同時に行われる)。独立したそれぞれの軸移動に反応して直線が直ちに描画される場合、マウスを斜めに動かすと、直角に交わる 2 直線が描画されることになる。

アプリケーションの応答前に、両軸における移動が計算に入れられていることを確認するには、x 軸項目と y 軸項目のシーケンス番号をチェックする方法もある。番号が同一であれば、2 つのイベントが同時に発生したことになる。詳細については、「タイム スタンプとシーケンス番号」を参照すること。

その他の DirectInput チュートリアルについては、「DirectInput C/C++ チュートリアル」を参照すること。