下面的代码处理所有的窗口消息。当我们注册好窗口类之后,程序跳转到这部分代码处理窗口消息。

  1. LRESULT CALLBACK WndProc( HWND hWnd, // 窗口的句柄
  2. UINT uMsg, // 窗口的消息
  3.  
  4. WPARAM wParam, // 附加的消息内容
  5.  
  6. LPARAM lParam) // 附加的消息内容
  7. {

下面的代码比对uMsg的值,然后转入case处理,uMsg 中保存了我们要处理的消息名字。

  1.   switch (uMsg) // 检查Windows消息
  2. {

如果uMsg等于WM_ACTIVE,查看窗口是否仍然处于激活状态。如果窗口已被最小化,将变量active设为FALSE。如果窗口已被激活,变量active的值为TRUE。

  1. case WM_ACTIVATE: // 监视窗口激活消息
  2. {
  3. if (!HIWORD(wParam)) // 检查最小化状态
  4. {
  5. active=TRUE; // 程序处于激活状态
  6. }
  7. else
  8. {
  9. active=FALSE; // 程序不再激活
  10. }
  11. return 0; // 返回消息循环
  12. }

如果消息是WM_SYSCOMMAND(系统命令),再次比对wParam。如果wParam 是 SC_SCREENSAVE 或 SC_MONITORPOWER的话,不是有屏幕保护要运行,就是显示器想进入节电模式。返回0可以阻止这两件事发生。

  1. case WM_SYSCOMMAND: // 系统中断命令
  2. {
  3. switch (wParam) // 检查系统调用
  4. {
  5. case SC_SCREENSAVE: // 屏保要运行?
  6.  
  7. case SC_MONITORPOWER: // 显示器要进入节电模式?
  8.  
  9. return 0; // 阻止发生
  10. }
  11. break; // 退出
  12. }

如果 uMsg是WM_CLOSE,窗口将被关闭。我们发出退出消息,主循环将被中断。变量done被设为TRUE,WinMain()的主循环中止,程序关闭。

  1. case WM_CLOSE: // 收到Close消息?
  2. {
  3. PostQuitMessage(0); // 发出退出消息
  4.  
  5. return 0; // 返回
  6. }

如果键盘有键按下,通过读取wParam的信息可以找出键值。我将键盘数组keys[ ]相应的数组组成员的值设为TRUE。这样以后就可以查找key[ ]来得知什么键被按下。允许同时按下多个键。

  1. case WM_KEYDOWN: // 有键按下么?
  2. {
  3. keys[wParam] = TRUE; // 如果是,设为TRUE
  4.  
  5. return 0; // 返回
  6. }

同样,如果键盘有键释放,通过读取wParam的信息可以找出键值。然后将键盘数组keys[ ]相应的数组组成员的值设为FALSE。这样查找key[ ]来得知什么键被按下,什么键被释放了。键盘上的每个键都可以用0-255之间的一个数来代表。举例来说,当我们按下40所代表的键时,keys[40]的值将被设为TRUE。放开的话,它就被设为FALSE。这也是key数组的原理。

  1. case WM_KEYUP: // 有键放开么?
  2. {
  3. keys[wParam] = FALSE; // 如果是,设为FALSE
  4.  
  5. return 0; // 返回
  6. }

当调整窗口时,uMsg 最后等于消息WM_SIZE。读取lParam的LOWORD 和HIWORD可以得到窗口新的宽度和高度。将他们传递给ReSizeGLScene(),OpenGL场景将调整为新的宽度和高度。

  1. case WM_SIZE: // 调整OpenGL窗口大小
  2. {
  3. ReSizeGLScene(LOWORD(lParam),HIWORD(lParam)); // LoWord=Width,HiWord=Height
  4.  
  5. return 0; // 返回
  6. }
  7. }

其余无关的消息被传递给DefWindowProc,让Windows自行处理。

  1. // 向 DefWindowProc传递所有未处理的消息。
  2.  
  3. return DefWindowProc(hWnd,uMsg,wParam,lParam);
  4. }

下面是我们的Windows程序的入口。将会调用窗口创建例程,处理窗口消息,并监视人机交互。

  1. int WINAPI WinMain( HINSTANCE hInstance, // 当前窗口实例

  2. HINSTANCE hPrevInstance, // 前一个窗口实例
  3.  
  4. LPSTR lpCmdLine, // 命令行参数
  5.  
  6. int nCmdShow) // 窗口显示状态
  7. {

我们设置两个变量。msg 用来检查是否有消息等待处理。done的初始值设为FALSE。这意味着我们的程序仍未完成运行。只要程序done保持FALSE,程序继续运行。一旦done的值改变为TRUE,程序退出。

  1. MSG msg; // Windowsx消息结构
  2.  
  3. BOOL done=FALSE; // 用来退出循环的Bool 变量

这段代码完全可选。程序弹出一个消息窗口,询问用户是否希望在全屏模式下运行。如果用户单击NO按钮,fullscreen变量从缺省的TRUE改变为FALSE,程序也改在窗口模式下运行。

  1. // 提示用户选择运行模式
  2.  
  3. if (MessageBox(NULL,"你想在全屏模式下运行么?", "设置全屏模式",MB_YESNO|MB_ICONQUESTION)==IDNO)
  4. {
  5. fullscreen=FALSE; // FALSE为窗口模式
  6. }

接着创建OpenGL窗口。CreateGLWindow函数的参数依次为标题、宽度、高度、色彩深度,以及全屏标志。就这么简单!我很欣赏这段代码的简洁。如果未能创建成功,函数返回FALSE。程序立即退出。

  1. // 创建OpenGL窗口
  2.  
  3. if (!CreateGLWindow("NeHe's OpenGL程序框架",640,480,16,fullscreen))
  4. {
  5. return 0; // 失败退出
  6. }

下面是循环的开始。只要done保持FALSE,循环一直进行。

  1. while(!done) // 保持循环直到 done=TRUE
  2. {

我们要做的第一件事是检查是否有消息在等待。使用PeekMessage()可以在不锁住我们的程序的前提下对消息进行检查。许多程序使用GetMessage(),也可以很好的工作。但使用GetMessage(),程序在收到paint消息或其他别的什么窗口消息之前不会做任何事。

  1. if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // 有消息在等待吗?
  2. {

下面的代码查看是否出现退出消息。如果当前的消息是由PostQuitMessage(0)引起的WM_QUIT,done变量被设为TRUE,程序将退出。

  1. if (msg.message==WM_QUIT) // 收到退出消息?
  2. {
  3. done=TRUE; // 是,则done=TRUE
  4. }
  5. else // 不是,处理窗口消息
  6. {

如果不是退出消息,我们翻译消息,然后发送消息,使得WndProc() 或 Windows能够处理他们。

  1. TranslateMessage(&msg); // 翻译消息
  2.  
  3. DispatchMessage(&msg); // 发送消息
  4. }
  5. }
  6. else // 如果没有消息
  7. {

如果没有消息,绘制我们的OpenGL场景。代码的第一行查看窗口是否激活。如果按下ESC键,done变量被设为TRUE,程序将会退出。

  1. // 绘制场景。监视ESC键和来自DrawGLScene()的退出消息
  2.  
  3. if (active) // 程序激活的么?
  4. {
  5. if (keys[VK_ESCAPE]) // ESC 按下了么?
  6. {
  7. done=TRUE; // ESC 发出退出信号
  8. }
  9. else // 不是退出的时候,刷新屏幕
  10. {

如果程序是激活的且ESC没有按下,我们绘制场景并交换缓存(使用双缓存可以实现无闪烁的动画)。我们实际上在另一个看不见的"屏幕"上绘图。当我们交换缓存后,我们当前的屏幕被隐藏,现在看到的是刚才看不到的屏幕。这也是我们看不到场景绘制过程的原因。场景只是即时显示。

  1. DrawGLScene(); // 绘制场景
  2.  
  3. SwapBuffers(hDC); // 交换缓存 (双缓存)
  4. }
  5. }

下面的一点代码是最近新加的(05-01-00)。允许用户按下F1键在全屏模式和窗口模式间切换。

  1. if (keys[VK_F1]) // F1键按下了么?
  2. {
  3. keys[VK_F1]=FALSE; // 若是,使对应的Key数组中的值为 FALSE
  4.  
  5. KillGLWindow(); // 销毁当前的窗口
  6.  
  7. fullscreen=!fullscreen; // 切换 全屏 / 窗口 模式
  8.  
  9. // 重建 OpenGL 窗口
  10.  
  11. if (!CreateGLWindow("NeHe's OpenGL 程序框架",640,480,16,fullscreen))
  12. {
  13. return 0; // 如果窗口未能创建,程序退出
  14. }
  15. }
  16. }
  17. }

如果done变量不再是FALSE,程序退出。正常销毁OpenGL窗口,将所有的内存释放,退出程序。

  1. // 关闭程序
  2.  
  3. KillGLWindow(); // 销毁窗口
  4.  
  5. return (msg.wParam); // 退出程序
  6. }

在这一课中,我已试着尽量详细解释一切。每一步都与设置有关,并创建了一个全屏OpenGL程序。当您按下ESC键程序就会退出,并监视窗口是否激活。我花了整整2周时间来写代码,一周时间来改正BUG并讨论编程指南,2天( 整整22小时来写HTML文件)。如果您有什么意见或建议请给我EMAIL。如果您认为有什么不对或可以改进,请告诉我。我想做最好的OpenGL教程并对您的反馈感兴趣。

第01课 OpenGL窗口(4)的更多相关文章

  1. 第01课 OpenGL窗口(3)

    接下来的代码段创建我们的OpenGL窗口.我花了很多时间来做决定是否创建固定的全屏模式这样不需要许多额外的代码,还是创建一个容易定制的友好的窗口但需要更多的代码.当然最后我选择了后者.我经常在EMai ...

  2. 第01课 OpenGL窗口(1)

    教程的这一节在2000年一月彻底重写了一遍.将会教您如何设置一个 OpenGL窗口.它可以只是一个窗口或是全屏幕的.可以任意 大小.任意色彩深度.此处的代码很稳定且很强大,您可以在您所有的OpenGL ...

  3. 第01课 OpenGL窗口(2)

    下一段包括了所有的绘图代码.任何您所想在屏幕上显示的东东都将在此段代码中出现.以后的每个教程中我都会在例程的此处增加新的代码.如果您对OpenGL已经有所了解的话,您可以在 glLoadIdentit ...

  4. NeHe OpenGL教程 第一课:OpenGL窗口

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  5. (7)nehe教程1 创建一个OpenGL窗口:

    不要用那个nehe ndk了 误人子弟! 转自: 一个窗口,代码可真多啊 http://www.yakergong.net/nehe/ 在这个教程里,我将教你在Windows环境中创建OpenGL程序 ...

  6. 第09课 OpenGL 移动图像

    3D空间中移动图像: 你想知道如何在3D空间中移动物体,你想知道如何在屏幕上绘制一个图像,而让图像的背景色变为透明,你希望有一个简单的动画.这一课将教会你所有的一切.前面的课程涵盖了基础的OpenGL ...

  7. 第04课 OpenGL 旋转

    旋转: 在这一课里,我将教会你如何旋转三角形和四边形.左图中的三角形沿Y轴旋转,四边形沿着X轴旋转. 上一课中我教给您三角形和四边形的着色.这一课我将教您如何将这些彩色对象绕着坐标轴旋转.其实只需在上 ...

  8. 第03课 OpenGL 添加颜色

    添加颜色: 作为第二课的扩展,我将叫你如何使用颜色.你将理解两种着色模式,在下图中,三角形用的是光滑着色,四边形用的是平面着色 上一课中我教给您三角形和四边形的绘制方法.这一课我将教您给三角形和四边形 ...

  9. 第02课 OpenGL 多边形

    你的第一个多边形: 在第一个教程的基础上,我们添加了一个三角形和一个四边形.也许你认为这很简单,但你已经迈出了一大步,要知道任何在OpenGL中绘制的模型都会被分解为这两种简单的图形.读完了这一课,你 ...

随机推荐

  1. SpringCloudAlibaba - 整合 Nacos 实现服务注册与发现

    目录 前言 环境 Nacos是什么? 服务发现原理 搭建 Nacos Server Nacos Server 下载地址 Nacos Server 的版本选择 运行 Nacos Server Nacos ...

  2. python学习笔记(九)-函数2

    交换两个变量的值 a = 2 b = 1 b = 1 a = 2 #方式一: b,a = a,b #交换两个变量的值 print(a,b) #方式二: a = a + b #3 b = a - b # ...

  3. Jmeter通过正则表达式提取器提取响应结果数据

    Jmeter进行接口测试常常会运到一个问题:就是第二个请求如何接收上一个请求响应中的参数.比如,现在个学生金币充值的接口,得先调用登录接口然后从返回里面复制一下sign的值,放到cookie里这样才能 ...

  4. django 高级扩展-中间件-上传图片-分页-富文本-celery

    """ django 高级扩展 一.静态文件 1.css,js,json,图片,字体等 2.配置setting,在最底下设置静态文件目录,写入下面代码 #配置静态文件目录 ...

  5. Hive On Spark保姆级攻略

    声明: 此博客参考了官网的配置方式,并结合笔者在实践网上部分帖子时的踩坑经历整理而成 这里贴上官方配置说明: [官方]: https://cwiki.apache.org//confluence/di ...

  6. CF710F-String Set Queries【AC自动机,二进制分组】

    正题 题目链接:https://www.luogu.com.cn/problem/CF710F 题目大意 \(T\)次操作 往集合中加入一个字符串 往集合中删除一个字符串 给出一个模式串求出现的集合里 ...

  7. WPF进阶技巧和实战03-控件(5-列表、树、网格04)

    ListView控件 ListView继承自简单的没有特色的ListBox,增加了对基于列显示的支持,并增加了快速切换视图或显示模式的能力,而不需要重新绑定数据以重新构建列表. ListView类继承 ...

  8. 【理解OS】1.保护模式概述

    这个系列文章主要目的是为了记录我个人学习保护模式后的总结与一点点的思考.我也是一个学习者,其中由错误在所难免,若各位朋友指出将不胜感激. 1. Intel CPU的运行模式概述 这里我将粗略介绍Int ...

  9. 使用Jacoco统计服务端代码覆盖情况实践

    一.背景 随着需求的迭代,需求增加的同时,有可能会伴随着一些功能的下线.如果不对系统已经不用的代码进行梳理并删除不需要的代码,那么就会增加系统维护成本以及理解成本.但经历比较长的迭代以及系统交接,可能 ...

  10. Google Chrome打开权限设置开关(摄像头,录音等)

    在搜索框输入以下字符 chrome://flags/#unsafely-treat-insecure-origin-as-secure