2.2.1 Win32 SDK风格的框架(4)
下面这些代码WndProc()函数处理所有的窗口消息。当注册好窗口类之后,程序跳转到这部分代码处理窗口消息。消息的处理方法是按照switch case结构来进行的,如本程序第300行根据消息的名字(它保存在uMsg参数中)来进行分类处理。
第303行如果uMsg等于WM_ACTIVATE,则查看窗口是否仍然处于激活状态,如果窗口已被最小化,将变量active设为FALSE;如果窗口已被激活,变量active的值为TRUE。下面第317行如果消息是WM_SYSCOMMAND(系统命令),再次比对wParam,如果wParam 是 SC_SCREENSAVE 或 SC_MONITORPOWER,不是有屏幕保护要运行,就是显示器准备进入节电模式,此处返回0可以阻止这两类事件发生。
如果 uMsg是WM_CLOSE,窗口将被关闭,并向消息循环发出退出消息,主循环将被中断,同时WndProc函数返回0,WinMain()的主循环中止,程序关闭。
下面第334行检查键盘按键消息,我们将键盘上的每个键用0~255之间的一个数来作为与其对应的key数组数据项的索引。举例来说,当我们按下40所代表的键时,keys[40]的值将被设为TRUE,如果该键释放,它就被设为FALSE,这也是key数组的原理。如果键盘有键按下,通过读取wParam的信息可以找出键值,我将键盘数组keys[]相应的数组组成员的值设为TRUE,这样以后就可以查找key[]来得知什么键被按下,允许同时按下多个键。同样地第340行检查按键释放消息,也通过读取wParam的信息可以找出键值。然后将键盘数组keys[]相应的数组组成员的值设为FALSE,这样查找key[]来得知什么键被按下,什么键被释放了。
当调整窗口时,uMsg等于WM_SIZE消息,通过读取lParam的LOWORD 和HIWORD可以得到窗口新的宽度和高度,然后将它们传递给ReSizeGLScene(),OpenGL场景将调整为新的宽度和高度。最后第355行调用函数DefWindowProc()将其他的消息交给Windows按默认方式处理。
下面是我们的Windows程序的入口即WinMain()函数,任何程序都需要一个入口函数,它和控制台程序的main()函数的作用是一样的。它将会调用窗口创建例程,处理窗口消息,并进行人机交互。在第363和364行定义了两个变量msg和done,其中msg用来检查是否有消息等待处理;done标志程序是否完成运行,其初始值设为FALSE,只要程序done保持FALSE,程序继续运行,一旦done的值改变为TRUE,程序就退出。
第367行弹出一个对话框,询问是否在全屏模式下运行,如果用户单击NO按钮,fullscreen变量从默认的TRUE改变为FALSE,程序也改在窗口模式下运行。再往下面创建OpenGL窗口。CreateGLWindow函数的参数依次为标题、宽度、高度、色彩深度,以及全屏标志,这段代码非常简洁。如果未能创建成功,函数返回FALSE,程序立即退出。
从第378行开始是循环的开始,只要done保持为FALSE,则循环一直进行。我们要做的第一件事是检查是否有消息在等待。在Windows的内部,GetMessage()和PeekMessage()执行着相同的代码,而两者最大的不同之处则体现在没有任何消息返回到应用程序的情况下。在此种情况下,PeekMessage()会返回一个空值到应用程序,而GetMessage()会在此时让应用程序休眠。我们这里使用PeekMessage()可以在不锁住程序的前提下对消息进行检查,如果我们使用GetMessage(),那么程序在收到WM_PAINT息或其他别的什么窗口消息之前不会做任何事情。
第382行查看是否出现退出消息,如果当前的消息是由PostQuitMessage(0)引起的WM_QUIT,done变量被设为TRUE,程序将退出;如果不是退出消息,我们翻译消息,然后发送消息,使得WndProc()或 Windows能够处理它们。
如果没有消息,上面这一段代码将绘制OpenGL场景。首先第395行检查窗口是否处于激活状态,接下来检查ESC键是否被按下,若按下则将done变量被设为TRUE,程序退出。如果程序是激活的而且ESC没有被按下,我们将绘制场景并交换缓存(使用双缓存可以实现无闪烁的动画),在双缓存技术中我们首先在一个看不见的“屏幕”(被称为后台缓存)上绘图,等其绘制完成后把它交换到屏幕(称为前台缓存)上进行显示,这也是我们看不到场景绘制过程的原因。
从第408行开始的这段代码允许用户按下F1键在全屏模式和窗口模式间切换,程序最后正常销毁OpenGL窗口,将所有的内存释放并退出程序。