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

LRESULT CALLBACK WndProc(    HWND    hWnd,                    // 窗口的句柄
UINT uMsg, // 窗口的消息 WPARAM wParam, // 附加的消息内容 LPARAM lParam) // 附加的消息内容
{

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

  switch (uMsg)                                // 检查Windows消息
{

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

        case WM_ACTIVATE:                        // 监视窗口激活消息
{
if (!HIWORD(wParam)) // 检查最小化状态
{
active=TRUE; // 程序处于激活状态
}
else
{
active=FALSE; // 程序不再激活
}
return 0; // 返回消息循环
}

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

        case WM_SYSCOMMAND:                        // 系统中断命令
{
switch (wParam) // 检查系统调用
{
case SC_SCREENSAVE: // 屏保要运行? case SC_MONITORPOWER: // 显示器要进入节电模式? return 0; // 阻止发生
}
break; // 退出
}

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

        case WM_CLOSE:                            // 收到Close消息?
{
PostQuitMessage(0); // 发出退出消息 return 0; // 返回
}

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

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

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

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

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

        case WM_SIZE:                            // 调整OpenGL窗口大小
{
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam)); // LoWord=Width,HiWord=Height return 0; // 返回
}
}

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

    // 向 DefWindowProc传递所有未处理的消息。

    return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

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

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

HINSTANCE hPrevInstance, // 前一个窗口实例 LPSTR lpCmdLine, // 命令行参数 int nCmdShow) // 窗口显示状态
{

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

    MSG    msg;                                // Windowsx消息结构

    BOOL    done=FALSE;                            // 用来退出循环的Bool 变量

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

    // 提示用户选择运行模式

    if (MessageBox(NULL,"你想在全屏模式下运行么?", "设置全屏模式",MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=FALSE; // FALSE为窗口模式
}

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

    // 创建OpenGL窗口

    if (!CreateGLWindow("NeHe's OpenGL程序框架",640,480,16,fullscreen))
{
return 0; // 失败退出
}

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

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

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

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

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

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

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

                TranslateMessage(&msg);                // 翻译消息

                DispatchMessage(&msg);                // 发送消息
}
}
else // 如果没有消息
{

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

            // 绘制场景。监视ESC键和来自DrawGLScene()的退出消息

            if (active)                        // 程序激活的么?
{
if (keys[VK_ESCAPE]) // ESC 按下了么?
{
done=TRUE; // ESC 发出退出信号
}
else // 不是退出的时候,刷新屏幕
{

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

                    DrawGLScene();                // 绘制场景

                    SwapBuffers(hDC);            // 交换缓存 (双缓存)
}
}

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

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

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

    // 关闭程序

    KillGLWindow();                                // 销毁窗口

    return (msg.wParam);                            // 退出程序
}

在这一课中,我已试着尽量详细解释一切。每一步都与设置有关,并创建了一个全屏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. CodeForce-782B The Meeting Place Cannot Be Changed(高精度二分)

    https://vjudge.net/problem/CodeForces-782B B. The Meeting Place Cannot Be Changed time limit per tes ...

  2. 使用git克隆仓库到本地报错:SSL certificate problem: unable to get local issuer certificate

    第一次使用Git工具克隆仓库,使用的是HTTPS链接,失败了.发现是因为通过HTTPS访问时,如果服务器上的SSL证书未经过第三方机构认证,Git就会报错. 解决方法:通过命令关闭验证 git con ...

  3. Java跨平台原理(字节码文件,虚拟机)

    介绍 C/C++语言都直接编译成针对特定平台机器码.如果要跨平台,需要使用相应的编译器重新编译. Java源程序(.java)要先编译成与平台无关的字节码文件(.class),然后字节码文件再解释成机 ...

  4. contos 7修改root密码

    https://www.linuxidc.com/Linux/2018-01/150211.htm 下面是CentOS 7的root密码修改 开机按esc 选择CentOS Linux (3.10.0 ...

  5. 『GoLang』接口

    接口是什么 Go 语言不是一种 "传统" 的面向对象编程语言:它里面没有类和继承的概念. 但是 Go 语言里有非常灵活的 接口 概念,通过它可以实现很多面向对象的特性.接口提供了一 ...

  6. [转载]SELinux安全系统基础

    链接:http://www.cnblogs.com/xiaoluo501395377/archive/2013/05/26/3100444.html 本篇随笔将记录一下学习SELinux的一些心得与体 ...

  7. 小记SpringMVC与SpringBoot 的controller的返回json数据的不同

    近期由于项目的改动变更,在使用springmvc和springboot测试的时候发现一个有趣的现象 1.springmvc的controller使用@ResponseBody返回的仅仅是json格式的 ...

  8. Vue组件间的数据传输

    1.父组件向子组件传输数据:自定义属性 1 //父组件 2 <Son :msg="message" :user="userinfo"></So ...

  9. Serverless 是一种思想状态

    来源 | Serverless 公众号:作者 | Ben Kehoe:译者 | donghui 函数不是重点 如果你因为喜欢 Lambda 而选择 Serverless,你这样做的原因是错误的.如果你 ...

  10. C++ IDE或编辑器安装

    IDE介绍 上节课我们讲了C++编译器,可是没有好的编辑器,只用记事本打代码,这谁受得了.Linux vim至少还有代码高亮(即我作文里经常会出现的"彩色的代码"),记事本连高亮都 ...