Platform SDK: DirectX

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

[Visual Basic]

ここでは、C++ でのアプリケーション開発について説明する。Visual Basic については、「DirectInput Visual Basic チュートリアル」を参照すること。

[C++]

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

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

まず、この関数は、元のカーソル位置が完全に消去されていることを確認する。Scrawl は独自のカーソルを維持し、その描画や消去を全面的に行わなければならないことに注意する。

void Scrawl_OnMouseInput(HWND hwnd)
{
/* 元のカーソルは、クリアされるので無効になる。
InvalidateCursorRect(hwnd);
 

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

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

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

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

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

ここで、アプリケーションは、デバイスに対するアクセス権が失われているかどうかの確認チェックを行い、失われていればすぐに適切なタイミングでマウスの再取得を行うように自身に指示する。この手順は、「ステップ 5 : マウスへのアクセス権の管理」で説明する。

        if (hr == DIERR_INPUTLOST) {
            PostMessage(hwnd, WM_SYNCACQUIRE, 0, 0L);
            break;
        }
 

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

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

ここまで実行が進めば、すべて順調である。すなわち、呼び出しは成功し、バッファにはデータ項目が存在する。ここで、アプリケーションは、DIDEVICEOBJECTDATA 構造体の dwOfs メンバを調べ、デバイス上のどのオブジェクトが状態変化を報告したかを確認する。次に、ヘルパー関数を呼び出し、適切に対応する。dwData メンバの値は、状態に関する情報をもっており、これらのヘルパー関数に渡される。

        /* 要素を調べて何が発生したかを確認する。*/
 
        switch (od.dwOfs) {
 
        /* DIMOFS_X:マウスの水平動作。*/
        case DIMOFS_X: UpdateCursorPosition(od.dwData, 0); break;
 
        /* DIMOFS_Y:マウスの垂直動作。*/
        case DIMOFS_Y: UpdateCursorPosition(0, od.dwData); break;
 
        /* DIMOFS_BUTTON0:ボタン 0 を押すあるいは離す。*/
        case DIMOFS_BUTTON0:
 
            if (od.dwData & 0x80) { /* ボタンを押す。*/
                fDone = 1;
                Scrawl_OnButton0Down(hwnd); /* ボタン押下モードに
                                               変わる。*/
            }
            break;
 
        /* DIMOFS_BUTTON1:ボタン 1 を押すまたは離す。*/
        case DIMOFS_BUTTON1:
 
            if (!(od.dwData & 0x80)) {    /* ボタンを離す。*/
                fDone = 1;
                Scrawl_OnButton1Up(hwnd); /* コンテキスト メニュー タイム。*/
            }
        }
 
    }
 

最後に、カーソルがいずれかのヘルパー関数により移動された場合のために、Scrawl_OnMouseInput サンプル関数は、カーソルが占有している画面上の矩形を無効化する。

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

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

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

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