ウインドウプロシージャが書ければ、キー入力を受け取るのも簡単です。 Xの場合と同じように、Escapeキーで終了するようにしてみます。
0001: /*
0002: key.c
0003: gcc key.c -mwindows
0004: bcc32 -W key.c
0005: */
0006:
0007: #include <windows.h>
0008:
0009: LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );
0010:
0011: int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
0012: LPSTR lpCmdLine, int nCmdShow )
0013: {
0014: HWND hWnd;
0015: MSG msg;
0016: WNDCLASS wndClass;
0017: static TCHAR szAppName[] = TEXT("Key");
0018: int ret;
0019:
0020: /* ウインドウクラスの設定 */
0021: wndClass.style = CS_HREDRAW|CS_VREDRAW;
0022: wndClass.lpfnWndProc = WndProc;
0023: wndClass.cbClsExtra = 0;
0024: wndClass.cbWndExtra = 0;
0025: wndClass.hInstance = hInstance;
0026: wndClass.hIcon = NULL;
0027: wndClass.hCursor = NULL;
0028: wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
0029: wndClass.lpszMenuName = NULL;
0030: wndClass.lpszClassName = szAppName;
0031:
0032: RegisterClass( &wndClass ); /* ウインドウクラスの登録 */
0033:
0034: hWnd = CreateWindow( /* ウインドウの作成 */
0035: szAppName, /* 作成するウインドウのクラス名 */
0036: TEXT("openwin-win32"), /* ウインドウタイトル */
0037: WS_OVERLAPPEDWINDOW, /* ウインドウスタイル */
0038: CW_USEDEFAULT, /* 左上頂点のX座標 */
0039: CW_USEDEFAULT, /* 左上頂点のY座標 */
0040: CW_USEDEFAULT, /* ウインドウの幅 */
0041: CW_USEDEFAULT, /* ウインドウの高さ */
0042: NULL, /* 親ウインドウのハンドル */
0043: NULL, /* メニューのハンドル */
0044: hInstance, /* 親モジュールのインスタンスハンドル */
0045: NULL ); /* ウインドウへの引数 */
0046:
0047: ShowWindow( hWnd, nCmdShow ); /* ウインドウの表示 */
0048:
0049: UpdateWindow( hWnd ); /* 描画領域の更新 */
0050:
0051: /* メッセージループ */
0052: while( (ret = GetMessage( &msg, NULL, 0, 0)) != 0 ){
0053: if( ret != -1 ){
0054: TranslateMessage( &msg );
0055: DispatchMessage( &msg );
0056: }
0057: }
0058:
0059: return msg.wParam;
0060: }
0061:
0062: /* ウインドウプロシージャ */
0063: LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ){
0064: HDC hdc;
0065: PAINTSTRUCT ps;
0066: HPEN hPen;
0067: RECT rect;
0068:
0069: switch( message ){
0070: case WM_PAINT:
0071: hdc = BeginPaint( hWnd, &ps ); /* 描画開始 */
0072:
0073: GetClientRect( hWnd, &rect ); /* クライアント領域の取得 */
0074:
0075: hPen = GetStockObject( BLACK_PEN ); /* ペンの選択 */
0076: SelectObject( hdc, hPen ); /* ペンの設定 */
0077:
0078: MoveToEx( hdc, 0, 0, NULL ); /* ペン座標を原点へ */
0079: LineTo( hdc, rect.right, rect.bottom ); /* 対角線を描画 */
0080:
0081: EndPaint( hWnd, &ps ); /* 描画終了 */
0082: return 0;
0083: case WM_KEYDOWN:
0084: switch( wParam ){
0085: case VK_ESCAPE:
0086: SendMessage( hWnd, WM_DESTROY, 0, 0 );
0087: }
0088: return 0;
0089: case WM_DESTROY:
0090: PostQuitMessage( 0 );
0091: return 0;
0092: }
0093: return DefWindowProc( hWnd, message, wParam, lParam );
0094: }
一番重要なのは、メッセージループ中のTranslateMessageです。
0051: /* メッセージループ */
0052: while( (ret = GetMessage( &msg, NULL, 0, 0)) != 0 ){
0053: if( ret != -1 ){
0054: TranslateMessage( &msg );
0055: DispatchMessage( &msg );
0056: }
0057: }
これまでは、DispatchMessageだけだったループの中にTranslateMessageが増えています。
このTranslateMessageが何を行っているかというと、「キーメッセージ」を「文字メッセージ」に変換してくれるのです。今回は「ESCAPEキーが押された」ことがわかればいいのと、ESCAPEキーは文字ではないのであまり意味はありませんが、たとえば、「a」のキーが押された場合に「キーメッセージ」だけで処理をしようとすると、シフトキーやその他のキーが押されていないかという点を考慮しなければなりません。つまり、自分でシフトキーが押されたかどうかを検出しておいて、その上で今押されたキーからどの文字が生成されるかを考える必要があります。しかし、「文字メッセージ」になっていれば、WM_CHARメッセージが来た時だけそのコードを調べればいいのです。
実は、国際化のためにはTranslateMessageがキーボードの違いを吸収してくれる、というか、適切に変換してくれる、などの仕事をしてくれています。
さて、そのキー検出の部分です。
0083: case WM_KEYDOWN:
0084: switch( wParam ){
0085: case VK_ESCAPE:
0086: SendMessage( hWnd, WM_DESTROY, 0, 0 );
0087: }
0088: return 0;
キーボードの動作もメッセージとして扱われています。ここでは「キーが押された」ということが検出できれば十分なので、WM_KEYDOWNメッセージを検出します。このとき、ウインドウプロシージャの第三引数に渡されるwParamに仮想キーコードが入っているのでこれがESCAPEキーのものかを調べています。
Xlib の方でタイトルバーからのイベントを受け取ることをしました。こちらでは通常だと「最小化」「最大化」「閉じる」のボタンがついています。こちらではこれを逆につかえなくする方法に触れておきます。
方法は二つあります。
wndClass.styleにCS_NOCLOSEを設定しておくと、ボタンが使えない状態になります。
CreateWindowの引数として、現在のプログラムでは WS_OVERLAPPEDWINDOWを指定していますが、これは WS_OVERLAPPED| WS_CAPTION| WS_SYSMENU| WS_THICKFRAME| WS_MINIMIZEBOX| WS_MAXIMIZEBOX を個々に設定したのと同じことを意味しています。 このうちWS_SYSMENUを設定しているとボタン類と左上隅のシステムメニューが現れるようになっています。