ウインドウプロシージャが書ければ、キー入力を受け取るのも簡単です。 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を設定しているとボタン類と左上隅のシステムメニューが現れるようになっています。