2014年3月28日金曜日

遠回りで学ぶWin32 APIでOpenGL 003

遠回りで学ぶWin32 APIでOpenGL 002の続き

ウィンドウサイズの調整

今のままだとウィンドウサイズが不定なので,AdjustWindowRect(MSDNのドキュメント)で希望の描画領域のサイズに合わせたウィンドウサイズを計算する.

// 007_adjust_window_rect.cpp
#include <tchar.h>
#include <Windows.h>
#include <iostream>
#include "using_console.h"

using namespace std;

void printLastError();

int WINAPI _tWinMain(
    HINSTANCE hInstance,
    HINSTANCE,
    LPTSTR lpCmdLine,
    int nCmdShow
)
{
    using_console uc;

    // ウィンドウクラスの登録
    WNDCLASSEX wcx = { 0 };
    wcx.cbSize = sizeof wcx;
    wcx.lpszClassName = TEXT("hello");
    wcx.lpfnWndProc = DefWindowProc;

    if(RegisterClassEx(&wcx) == 0){
        printLastError();
        cout << "終了します.何か入力してください." << endl;
        cin.get();
        return 0;
    }
    
    // ウィンドウサイズの計算
    LONG const WIDTH = 640;
    LONG const HEIGHT = 480;
    RECT r = { 0, 0, WIDTH, HEIGHT };
    DWORD windowStyle = WS_OVERLAPPEDWINDOW;
    if(!AdjustWindowRect(&r, windowStyle, FALSE)){
        cout << "終了します.何か入力してください." << endl;
        cin.get();
        return 0;
    }

    // ウィンドウの生成
    HWND hWnd = CreateWindow(
        wcx.lpszClassName,
        TEXT("hello"),
        windowStyle,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        r.right - r.left,
        r.bottom - r.top,
        NULL,
        NULL,
        hInstance,
        NULL
    );
    if(!hWnd){
        printLastError();
        cout << "終了します.何か入力してください." << endl;
        cin.get();
        return 0;
    }

    // ウィンドウの表示
    ShowWindow(hWnd, nCmdShow);

    cout << "終了します.何か入力してください." << endl;
    cin.get();

    return 0;
}

/*!
 *  GetLastError()によるエラーコードをFormatMessage()により
 *  文字列に変換し,標準出力に出力する.
 */
void printLastError()
{
    LPSTR pBuffer = NULL;
    DWORD error = GetLastError();
    DWORD result = FormatMessageA(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_IGNORE_INSERTS |
        FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        error,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        reinterpret_cast<LPSTR>(&pBuffer),
        0,
        NULL
    );

    if(result > 0 && pBuffer){
        cout << pBuffer << endl;
        LocalFree(pBuffer);
    }
}

コンパイル

cl /EHsc 007_adjust_window_rect.cpp using_console.cpp

メッセージループ

今のままだと何もできずに終了してしまうので,Windowsからマウスのクリックといったイベントを受け取るためのメッセージループを追加する.PeekMessage関数(MSDNのドキュメント)でメッセージの有無をチェックし,メッセージがあればDispatchMessage関数(MSDNのドキュメント)でウィンドウにメッセージを送る.メッセージが特に来ていない場合,Sleep関数(MSDNのドキュメント)でしばらく待機させる.

// 008_message_loop.cpp
#include <tchar.h>
#include <Windows.h>
#include <iostream>
#include "using_console.h"

using namespace std;

void printLastError();

int WINAPI _tWinMain(
    HINSTANCE hInstance,
    HINSTANCE,
    LPTSTR lpCmdLine,
    int nCmdShow
)
{
    using_console uc;

    /* ウィンドウクラスの登録からウィンドウの表示まで省略 */

    MSG msg = { 0 };
    while(msg.message != WM_QUIT){
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE){
            DispatchMessage(&msg);
        }else{
            Sleep(100);
        }
    }

    return msg.wParam;
}

/*!
 *  GetLastError()によるエラーコードをFormatMessage()により
 *  文字列に変換し,標準出力に出力する.
 */
void printLastError()
{
    /* 省略 */
}

実はこのままだとウィンドウを閉じてもプログラムが終了しないので,コンソール画面でCtrl+Cを押して強制終了する.

コンパイル

cl /EHsc 008_message_loop.cpp using_console.cpp

ウィンドウプロシージャ

ウィンドウを閉じた時にメッセージループを抜けるようにするため,独自のウィンドウプロシージャWindowProc(MSDNのドキュメント)を用意する.この関数は,型さえ合っていれば名前は何でも良い.最低限処理する必要があるのは,WM_DESTROYメッセージで,そのメッセージが送られてきたらPostQuitMessage関数(MSDNのドキュメント)でWM_QUITメッセージをイベントメッセージのキューに投げる.これで,ウィンドウを閉じるとメッセージループを抜けて終了するようになる.

// 009_window_proc.cpp
#include <tchar.h>
#include <Windows.h>
#include <iostream>
#include "using_console.h"

using namespace std;

LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
void printLastError();

int WINAPI _tWinMain(
    HINSTANCE hInstance,
    HINSTANCE,
    LPTSTR lpCmdLine,
    int nCmdShow
)
{
    using_console uc;

    WNDCLASSEX wcx = { 0 };
    wcx.cbSize = sizeof wcx;
    wcx.lpszClassName = TEXT("hello");
    wcx.lpfnWndProc = WindowProc;

    /* ウィンドウの生成からウィンドウの表示まで省略 */

    MSG msg = { 0 };
    while(msg.message != WM_QUIT){
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE){
            DispatchMessage(&msg);
        }else{
            Sleep(100);
        }
    }

    return msg.wParam;
}

LRESULT CALLBACK WindowProc(
    HWND hWnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam)
{
    switch(msg){
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, msg, wParam, lParam);
    }

    return 0;
}

/*!
 *  GetLastError()によるエラーコードをFormatMessage()により
 *  文字列に変換し,標準出力に出力する.
 */
void printLastError()
{
    /* 省略 */
}

コンパイル

cl /EHsc 009_window_proc.cpp using_console.cpp

第三回まとめ

  • クリックなどのイベントを処理するにはメッセージループが必要
  • メッセージループはPeekMessageでメッセージを確認し,DispatchMessageでウィンドウにメッセージを送る

次でようやくOpenGLの初期化に入れそう.

第四回へ

0 件のコメント:

コメントを投稿