如何对键盘消息控制:glut库、win32、MFC

来源:互联网 发布:淘宝上旗舰店和专卖店 编辑:程序博客网 时间:2024/05/18 02:51

三种情况下对键盘消息控制:glut库键盘消息、win32消息、MFC键盘消息。
一、glut库键盘消息
1.使用glutKeyboardFunc(keyboard)
int main(int argc, char** argv)
{
 glutInit(&argc, argv);
 glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
 glutInitWindowSize(250, 250);
 glutInitWindowPosition(100, 100);
 glutCreateWindow(argv[0]);
 init();
 glutDisplayFunc(display);
 glutReshapeFunc(reshape);
 glutKeyboardFunc(keyboard);
 glutMainLoop();
 return 0;
}

void keyboard (unsigned char key, int x, int y)
{
 switch (key)
 {
 case 'z':
  rotate = (rotate + 10) % 360;
  glutPostRedisplay();
  break;
 case 'Z':
  rotate = (rotate - 10) % 360;
  glutPostRedisplay();
  break;
 case 27:
  exit(0);
  break;
 default:
  break;
 }
}

2.使用glutSpecialFunc( SpecialKeys )
int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize (800, 600);
    glutInitWindowPosition (100, 100);
    glutCreateWindow( "Triangles Example" );
    glutDisplayFunc( RenderScene );
    glutIdleFunc (RenderScene); //glutIdleFunc 表示在CPU空闲的时间调用某一函数
    glutReshapeFunc( ChangeSize );
    glutSpecialFunc( SpecialKeys );
    SetupRC();
    glutMainLoop();
    return 1;
}
void SpecialKeys(int key, int x, int y)
{
    if(key == GLUT_KEY_UP)
        xRot -= 5.0f;
    if(key == GLUT_KEY_DOWN)
        xRot += 5.0f;
    if(key == GLUT_KEY_LEFT)
        yRot -= 5.0f;
    if(key == GLUT_KEY_RIGHT)
        yRot += 5.0f;
    if(xRot > 356.0f)
        xRot = 0.0f;
    if(xRot < -1.0f)
        xRot = 355.0f;
    if(yRot > 356.0f)
        yRot = 0.0f;
    if(yRot < -1.0f)
        yRot = 355.0f;
    glutPostRedisplay();// this will refresh the window, so, it works the same to call RenderScene() directly
}


二、win32消息
注册类的同时,有响应函数WndProc。至于捕捉消息、消息处理函数可以分开处理(如下例1),也可以一起处理(如例2)。
例1:

LRESULT CALLBACK WndProc( HWND hWnd,   // Handle For This Window
       UINT uMsg,   // Message For This Window
       WPARAM wParam,   // Additional Message Information
       LPARAM lParam)   // Additional Message Information
{
 switch (uMsg)         // Check For Windows Messages
 {
  case WM_ACTIVATE:       // Watch For Window Activate Message
  {
   if (!HIWORD(wParam))     // Check Minimization State
   {
    active=TRUE;      // Program Is Active
   }
   else
   {
    active=FALSE;      // Program Is No Longer Active
   }

   return 0;        // Return To The Message Loop
  }

  case WM_SYSCOMMAND:       // Intercept System Commands
  {
   switch (wParam)       // Check System Calls
   {
    case SC_SCREENSAVE:     // Screensaver Trying To Start?
    case SC_MONITORPOWER:    // Monitor Trying To Enter Powersave?
    return 0;       // Prevent From Happening
   }
   break;         // Exit
  }

  case WM_CLOSE:        // Did We Receive A Close Message?
  {
   PostQuitMessage(0);      // Send A Quit Message
   return 0;        // Jump Back
  }

  case WM_KEYDOWN:       // Is A Key Being Held Down?
  {
   keys[wParam] = TRUE;     // If So, Mark It As TRUE
   return 0;        // Jump Back
  }

  case WM_KEYUP:        // Has A Key Been Released?
  {
   keys[wParam] = FALSE;     // If So, Mark It As FALSE
   return 0;        // Jump Back
  }

  case WM_SIZE:        // Resize The OpenGL Window
  {
   ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));  // LoWord=Width, HiWord=Height
   return 0;        // Jump Back
  }
 }

 // Pass All Unhandled Messages To DefWindowProc
 return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
int WINAPI WinMain( HINSTANCE hInstance,   // Instance
     HINSTANCE hPrevInstance,  // Previous Instance
     LPSTR  lpCmdLine,   // Command Line Parameters
     int   nCmdShow)   // Window Show State
{
 MSG  msg;         // Windows Message Structure
 BOOL done=FALSE;        // Bool Variable To Exit Loop
 while(!done)         // Loop That Runs While done=FALSE
 {
  if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Is There A Message Waiting?
  {
   if (msg.message==WM_QUIT)    // Have We Received A Quit Message?
   {
    done=TRUE;       // If So done=TRUE
   }
   else         // If Not, Deal With Window Messages
   {
    TranslateMessage(&msg);    // Translate The Message
    DispatchMessage(&msg);    // Dispatch The Message
   }
  }
  else          // If There Are No Messages
  {
   // Draw The Scene.  Watch For ESC Key And Quit Messages From DrawGLScene()
   if ((active && !DrawGLScene()) || keys[VK_ESCAPE]) // Active?  Was There A Quit Received?
   {
    done=TRUE;       // ESC or DrawGLScene Signalled A Quit
   }
   else         // Not Time To Quit, Update Screen
   {
    SwapBuffers(hDC);     // Swap Buffers (Double Buffering)
    if (keys['T'] && !tp)
    {
     tp=TRUE;
     twinkle=!twinkle;
    }
    if (!keys['T'])
    {
     tp=FALSE;
    }

    if (keys[VK_UP])
    {
     tilt-=0.5f;
    }

    if (keys[VK_DOWN])
    {
     tilt+=0.5f;
    }

    if (keys[VK_PRIOR])
    {
     zoom-=0.2f;
    }

    if (keys[VK_NEXT])
    {
     zoom+=0.2f;
    }

    if (keys[VK_F1])      // Is F1 Being Pressed?
    {
     keys[VK_F1]=FALSE;     // If So Make Key FALSE
     KillGLWindow();      // Kill Our Current Window
     fullscreen=!fullscreen;    // Toggle Fullscreen / Windowed Mode
     // Recreate Our OpenGL Window
     if (!CreateGLWindow("NeHe's Animated Blended Textures Tutorial",640,480,16,fullscreen))
     {
      return 0;      // Quit If Window Was Not Created
     }
    }
   }
  }
 }

 // Shutdown
 KillGLWindow();         // Kill The Window
 return (msg.wParam);       // Exit The Program
}

例2:
LRESULT CALLBACK WinProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LONG    lRet = 0;
    PAINTSTRUCT    ps;

    switch (uMsg)
 {
    case WM_SIZE:       
  if(!g_bFullScreen)    
  {
   SizeOpenGLScreen(LOWORD(lParam),HIWORD(lParam));
   GetClientRect(hWnd, &g_rRect);    
  }
        break;
 
 case WM_PAINT:         
  BeginPaint(hWnd, &ps);      
  EndPaint(hWnd, &ps);     
  break;

 case WM_LBUTTONDOWN:        // 按下鼠标左键,改变绘制模式
  
  if(g_ViewMode == GL_TRIANGLES) {  
   g_ViewMode = GL_LINE_STRIP;  
  } else {
   g_ViewMode = GL_TRIANGLES; 
  }
  break;

 case WM_RBUTTONDOWN:        // 按下鼠标右键,改变光照模式
  
  g_bLighting = !g_bLighting;  

  if(g_bLighting) {     
   glEnable(GL_LIGHTING);   
  } else {
   glDisable(GL_LIGHTING);   
  }
  break;

 case WM_KEYDOWN:         // 键盘响应

  switch(wParam) {        
   case VK_ESCAPE:        // 按下ESC键
    PostQuitMessage(0);     
    break;

   case VK_LEFT:        // 按下向左键
    g_RotationSpeed -= 0.05f; 
    break;

   case VK_RIGHT:        // 按下向右键
    g_RotationSpeed += 0.05f;   
    break;
   case 84:        // 按下向右键
    g_RotationSpeed += 0.05f;   
    break;
  }
  break;

    case WM_CLOSE:         
        PostQuitMessage(0);      
        break;
    
    default:          
        lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
        break;
    }
 
    return lRet;          
}
main中:
 while(1)           
 {             
  if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
   if(msg.message == WM_QUIT)     
    break;
            TranslateMessage(&msg);      
            DispatchMessage(&msg);     
        }
  else         
  {
   RenderScene();      
        }
 }

三、MFC键盘消息
处理单键消息。如果当前焦点控件是按钮, 键盘事件会被按钮先处理掉了,不会传递到CMyDlg。要想实现你的想法, 需要重载 PreTranslateMessage。
1.
为了截获键盘击键的值,需要用到WM_CHAR消息。但在Project中添加该消息后会发现,程序无法响应该消息。即击键后程序并没有执行到该消息对应的函数处。参考MSDN对该消息的描述:
This member function is called by the framework to allow your application to handle a Windows message. The parameters passed to your function reflect the parameters received by the framework when the message was received. If you call the base-class implementation of this function, that implementation will use the parameters originally passed with the message and not the parameters you supply to the function.
关键的意思是要执行WM_CHAR消息,程序焦点必须在主窗口上。但不幸的是,程序运行以后,焦点在按钮上。解决的方式是使用PreTranslateMessage消息,进行处理,将焦点设置到主窗口上。具体代码如下:
BOOL CTest1_0Dlg::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class

if ( pMsg->message == WM_KEYDOWN ||pMsg->message == WM_CHAR)
{
   {
    pMsg->hwnd = m_hWnd;
      return FALSE;
   }
}
return CDialog::PreTranslateMessage(pMsg);
}
void CTest1_0Dlg::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch(nChar)
{
case 'x':
   OnButtonStop();
   break;
case 'g':
   OnButtonStart();
   break;
default:
   break;
}
CDialog::OnChar(nChar, nRepCnt, nFlags);
}
CWnd::OnChar
afx_msg void OnChar( UINT nChar, UINT nRepCnt, UINT nFlags );
或者直接
BOOL CKWOpenGL1Dlg::PreTranslateMessage(MSG* pMsg)
{
 if ( pMsg->message == WM_KEYDOWN )//pMsg->message == WM_CHAR||)//
 {
  switch(pMsg->wParam)
  {
  case VK_LEFT:     //表示是方向键中的向下的键
   {
       rotate=rotate+30.0;
   if(rotate>=360.0)
    rotate-=360.0;
   OnBnClickedOk();
    return FALSE;
   //break;
   }
  case VK_UP:      //表示是方向键中的向上的键
   break;
  }
 }
return CDialog::PreTranslateMessage(pMsg);
}
参数: nChar 包含了键的字符代码值。 
nRepCnt 包含了重复计数,当用户按下键时重复的击键数目。 
nFlags 包含了扫描码,键暂态码,以前的键状态以及上下文代码,如下面的列表所示: 值 含义 
0--15 指定了重复计数。其值是用户按下键时重复的击键数目 
16--23 指定了扫描码。其值依赖于原始设备制造商(OEM) 
24 指明该键是否是扩展键,如增强的101或102键盘上右边的ALT或CTRL键如果它是个扩展键,则该值为1;否则,值为0 
25--28 Windows内部使用 
29 指定了上下文代码。如果按键时ALT键是按下的,则该值为1;否则,值为0 
30 指定了以前的键状态。如果在发送消息前键是按下的,则值为1;如果键是弹起的,则值为0 
31 指定了键的暂态。如果该键正被放开,则值为1,如果键正被按下,则该值为0 

说明:
当击键被转换为非系统字符时,框架调用这个成员函数。这个函数是在OnKeyUp成员函数之前,OnKeyDown成员之后调用的。OnChar包含了被按下或放开的键值。
由于按键和产生的OnChar调用不必是一一对应的,因此nFlags中的信息对应用程序通常是没有用的。NFlags中的信息仅对最近在OnChar之前调用的OnKeyUp成员函数或OnKeyDown成员函数有用。
对于IBM增强型101和102键键盘,键盘的主体部分的增强键是右边的ALT和CTRL键;还有数字键盘左侧的INS,DEL,HOME,END,PAGE UP,PAGE DOWN以及箭头键等;以及数字键盘上的斜杠(/)和ENTER键。其它键盘可能会支持nFlags中的扩展键位。

注意:
框架调用这个成员函数以允许你的应用程序处理一个Windows消息。传递给你的成员函数的参数反映了接收到消息时框架接收到的参数。如果你调用了这个函数的基类实现,则该实现将使用最初传递给消息的参数(而不是你提供给这个函数的参数)。

2.
  在Windows程序中,消息是由MSG结构体来表示的。MSG结构体的定义如下(参见MSDN):
  typedef struct tagMSG {
  HWND hwnd;
  UINT message;
  WPARAM wParam;
  LPARAM lParam;
  DWORD time;
  POINT pt;
  } MSG;
  该结构体中各成员变量的含义如下:
  第一个成员变量hwnd表示消息所属的窗口。我们通常开发的程序都是窗口应用程序,一个消息一般都是与某个窗口相关联的。例如,在某个活动窗口中按下鼠标左键,产生的按键消息就是发给该窗口的。在Windows程序中,用HWND类型的变量来标识窗口。
  第二个成员变量message指定了消息的标识符。在Windows中,消息是由一个数值来表示的,不同的消息对应不同的数值。但是由于数值不便于记忆,所以Windows将消息对应的数值定义为WM_XXX宏(WM是Window Message的缩写)的形式,XXX对应某种消息的英文拼写的大写形式。例如,鼠标左键按下消息是WM_LBUTTONDOWN,键盘按下消息是WM_KEYDOWN,字符消息是WM_CHAR,等等。在程序中我们通常都是以WM_XXX宏的形式来使用消息的。
  提示:如果想知道WM_XXX消息对应的具体数值,可以在Visual C++开发环境中选中WM_XXX,然后单击鼠标右键,在弹出菜单中选择goto definition,即可看到该宏的具体定义。跟踪或查看某个变量的定义,都可以使用这个方法。
  第三、第四个成员变量wParam和lParam,用于指定消息的附加信息。例如,当我们收到一个字符消息的时候,message成员变量的值就是WM_CHAR,但用户到底输入的是什么字符,那么就由wParam和lParam来说明。wParam、lParam表示的信息随消息的不同而不同。如果想知道这两个成员变量具体表示的信息,可以在MSDN中关于某个具体消息的说明文档查看到。读者可以在VC++的开发环境中通过goto definition查看一下WPARAM和LPARAM这两种类型的定义,可以发现这两种类型实际上就是unsigned int和long。
  最后两个变量分别表示消息投递到消息队列中的时间和鼠标的当前位置。
为了不影响其他键盘消息,需要注意:只有X键才响应,因此不需要OnChar,如下:
BOOL CKWOpenGL1Dlg::PreTranslateMessage(MSG* pMsg)
{
 if(pMsg->message == WM_CHAR)
 {
  switch(pMsg->wParam)
  {
  case 'x':
  case 'X':
   {
   rotate=rotate+30.0;
   if(rotate>=360.0)
    rotate-=360.0;
   OnBnClickedOk();
    return FALSE;
   }
  }
 }
 return CDialog::PreTranslateMessage(pMsg);
}