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

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. 【OI】竖式问题分析与解答

    题目:找出所有形如abc*de(三位数乘以两位数)的算式,使得在完整的竖式中,所有数字都属于一个特定的数字集合.输入数字集合(相邻数字之间没有空格),输出所有竖式.每个竖式前应有编号,之后应有一个空行 ...

  2. Python - 生成requirement.text 文件

    前言 该篇操作笔记摘自小菠萝 Python项目中,一般都会有一个 requirements.txt 文件 这个文件主要是用于记录当前项目下的所有依赖包及其精确的版本号,以方便在一个新环境下更快的进行部 ...

  3. WireShark基础用法

    特点 免费 开源 跨平台 抓包原理 内部原理 抓取网卡 抓包环境 抓取本地数据 抓取外部数据 利用hub 流量镜像span.rspan.erspan 界面介绍.首选项.抓包选项 界面介绍 菜单栏 帮助 ...

  4. cgroup配置

    待续... https://docs.hortonworks.com/HDPDocuments/HDP3/HDP-3.1.0/data-operating-system/content/enablin ...

  5. django 模版-标签-视图-csrf-token-模版继承-HTML过滤器

    """ ******模版****** --定义模版-- **变量** 视图传递给模版的数据 注意1:要遵守标识符规则 语法:{{var(即变量)}} 如果使用的变量不存在 ...

  6. Android Kotlin协程入门

    Android官方推荐使用协程来处理异步问题.以下是协程的特点: 轻量:单个线程上可运行多个协程.协程支持挂起,不会使正在运行协程的线程阻塞.挂起比阻塞节省内存,且支持多个并行操作. 内存泄漏更少:使 ...

  7. P3649-[APIO2014]回文串【PAM】

    正题 题目链接:https://www.luogu.com.cn/problem/P3649 题目大意 一个字符串,求最大的回文串长度×出现次数 解题思路 构建出\(\text{PAM}\)然后统计一 ...

  8. CF280C-Game on Tree【数学期望】

    正题 题目链接:https://www.luogu.com.cn/problem/CF280C 题目大意 \(n\)个点的一棵树,每次选择一个没有染色的点把它和它的子树染黑,求期望全部染黑的步数. 解 ...

  9. 智汀家庭云-开发指南Golang:设备场景

    场景是指通过SA实现设备联动.例如,自动检测今天的天气情况,今天无雨,定时智能音箱播放浇花提醒,并且播报今天的天气情况. 根据自身需求,把多种控制并发的事情编辑成一个场景,并命名,可以通过场景控制很多 ...

  10. HTML对溢出的控制:overflow

    用于对超出模块的内容进行控制 overflow的三种参数 1.visible(默认):溢出部分可见 2.hidden:溢出部分隐藏 3.auto:适当加入滚动条 overflow使用方法 1.常用 o ...