キー入力を受け取る

ウインドウプロシージャが書ければ、キー入力を受け取るのも簡単です。 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が増えています。

BOOL TranslateMessage( const MSG *lpMsg );

この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.styleCS_NOCLOSEを設定しておくと、ボタンが使えない状態になります。

ウインドウスタイルで設定する

CreateWindowの引数として、現在のプログラムでは WS_OVERLAPPEDWINDOWを指定していますが、これは WS_OVERLAPPED| WS_CAPTION| WS_SYSMENU| WS_THICKFRAME| WS_MINIMIZEBOX| WS_MAXIMIZEBOX を個々に設定したのと同じことを意味しています。 このうちWS_SYSMENUを設定しているとボタン類と左上隅のシステムメニューが現れるようになっています。

Back to TOP