OpenGL学习1(基于SDK):全屏窗口,按ESC退出
1、windows程序框架,建立一个窗口。纯手打程序,自己加了一些注释,关于对这段程序的理解,欢迎和大家一起探讨
2、#include<windows.h>
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int iCmdShow)
{
WNDCLASS wndclass; //注册窗口类
wndclass.cbClsExtra=0;
wndclass.cbWndExtra=0;
wndclass.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH); //黑色背景
wndclass.hInstance=hInstance;
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW); //鼠标指针
wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION); //小图标
wndclass.lpfnWndProc=WndProc; //回调函数
wndclass.lpszClassName="HelloWin"; //类名
wndclass.lpszMenuName=NULL; //
wndclass.style=CS_HREDRAW|CS_VREDRAW; //垂直重绘和水平重绘
if(!RegisterClass(&wndclass)) //注册窗口
{
MessageBox(NULL,"注册窗口失败!","error",MB_OK);
return 0;
}
HWND hwnd=CreateWindow("HelloWin","HelloWin",WS_OVERLAPPEDWINDOW, //创建窗口
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,iCmdShow); //显示窗口
UpdateWindow(hwnd); //更新
MSG msg;
while(GetMessage(&msg,NULL,0,0)) //消息循环
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) //回调函数,各种消息
{
PAINTSTRUCT ps;
switch(msg)
{
case WM_CREATE:
return 0;
case WM_PAINT:
{
HDC hdc=BeginPaint(hWnd,&ps);
EndPaint(hWnd,&ps);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd,msg,wParam,lParam); //预处理消息
}
3、上面的程序,运行之后显示的是一个背景色为黑色的窗口

4、接下来我们实现下全屏显示。分为两步,一、修改显示设备的属性,二、对窗口样式做一些处理。
首先我们修改显示设备的属性,要用到的是LONG ChangeDisplaySettings( LPDEVMODE lpDevMode, DWORD dwflags );
第一个参数,指向一个DEVMODE结构,MSDN上的说明是 ”describes the graphics mode to switch to“
DEVMODE包含很多字段,我们在这里用到的有
dmSize(结构大小),
dmBitsPerPel(每个像素的字节位)
dmPelsWidth(屏幕宽),
dmPelsHeight(屏幕高度),
dmFields(位标记,说明哪些字段有效,在这里将它设置为”DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT“表示前面设置的,屏幕高、宽和像素字节有效)
在这里需要注意的是dmPelsWidth和dmPelsHeight的值,需要设置成和CreateWindow里面的宽,高一样,比如我们这里都设置成为800,600。
第二个参数,我们用 CDS_FULLSCREEN,在新的显示模式下,将任务栏从屏幕移除,并强制windows留下其它的屏幕部分
该函数的如果成功返回DISP_CHANGE_SUCCESSFUL,还有返回 DISP_CHANGE_RESTART,表示计算机需重启来让设置的显示模式工作,等等(详情看msdn吧~)
所以,修改显示设备属性的代码段如下:
5、 DEVMODE dmScreenSettings;
memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
dmScreenSettings.dmSize=sizeof(dmScreenSettings); //结构大小
dmScreenSettings.dmPelsHeight=600; //屏幕的高
dmScreenSettings.dmPelsWidth=800; //屏幕的宽
dmScreenSettings.dmBitsPerPel=16; //像素字节位数
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; //位标记,标明哪些字节是有效的,对应前面设置的三个参数
if(ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
{
if (MessageBox(NULL,"修改显示设备属性失败!是否继续用一般模式显示","提示",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
{
fullscreen=FALSE;
}
else
{
MessageBox(NULL,"点击关闭窗口","ERROR",MB_OK|MB_ICONSTOP);
return FALSE;
}
}
6、修改了显示设备的属性后,我们要对窗口样式做一些处理,如,去掉窗口的顶层/边框,还要把鼠标指针给隐藏起来(因为全屏的窗口这些东西都没有)
代码段如下:
7、 dwExStyle=WS_EX_APPWINDOW; //隐藏顶层的窗口
dwStyle=WS_POPUP; //没有边框的窗口
ShowCursor(FALSE); //不显示鼠标指针
8、这样全屏就实现了,下面来说下设置ESC退出程序,只要在回到函数的消息处理中添加按键处理消息WM_KEYDOWN就可以了==
1、当ESC被按下,我们发送WM_CLOSE应用消息
2、在消息循环中判断msg.message是否为WM_CLOSE。关于这个,为了理解清楚,查了下文档,GetMessage里面的msg是用来接受来自线程消息队列里的消息信息的(英文不好,所以还是:Pointer to an MSG structure that receives message information from the thread's message queue. )而,msg.message指定消息成员(Specifies the message number.)如果接受到了WM_CLOSE消息,就用break,结束消息循环
程序段如下
9、回调函数里添加(按照此方式写,方面添加其他的按键处理)
case WM_KEYDOWN:
switch(wParam)
{
case VK_ESCAPE:
PostMessage(hWnd,WM_CLOSE,0,0);
break;
}
return 0;
2、消息处理函数改为:
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
if(msg.message==WM_CLOSE)
{
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
10、好了这样就大功告成了,程序运行时没错的

11、尝试了下用截图软件,显示分辨率是800*600,800*600之外的不能截,话说我是不是要研究下截图软件了==
最后贴出完整的程序吧,里面添加了一些其他的样式(去掉那些样式也能正常运行显示,所以自己理解就好啦,不理解的尽量去理解,还没理解到自己想要的程序,就不用,哼!),加了注释了,应该比较好懂
12、#include<windows.h>
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int iCmdShow)
{
DWORD dwExStyle;
DWORD dwStyle;
WNDCLASS wndclass;
wndclass.cbClsExtra=0;
wndclass.cbWndExtra=0;
wndclass.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
wndclass.hInstance=hInstance;
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wndclass.lpfnWndProc=WndProc;
wndclass.lpszClassName="HelloWin";
wndclass.lpszMenuName=NULL;
wndclass.style=CS_HREDRAW|CS_VREDRAW;
if(!RegisterClass(&wndclass))
{
MessageBox(NULL,"注册失败!","error",MB_OK);
return 0;
}
BOOL fullscreen=true;
if(MessageBox(NULL,"是否全屏显示?","提示",MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=false;
}
if(fullscreen)
{
DEVMODE dmScreenSettings;
memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
dmScreenSettings.dmSize=sizeof(dmScreenSettings); //结构大小
dmScreenSettings.dmPelsHeight=600; //屏幕的高
dmScreenSettings.dmPelsWidth=800; //屏幕的宽
dmScreenSettings.dmBitsPerPel=16; //像素字节位数
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; //位标记,标明哪些字节是有效的,对应前面设置的三个参数
if(ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
{
if (MessageBox(NULL,"修改显示设备属性失败!是否继续用一般模式显示","提示",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
{
fullscreen=FALSE;
}
else
{
MessageBox(NULL,"点击关闭窗口","ERROR",MB_OK|MB_ICONSTOP);
return FALSE;
}
}
if(fullscreen)
{
dwExStyle=WS_EX_APPWINDOW; //隐藏顶层的窗口
dwStyle=WS_POPUP; //没有边框的窗口
ShowCursor(FALSE); //不显示鼠标指针
}
else
{
dwExStyle=WS_EX_APPWINDOW|WS_EX_WINDOWEDGE;
dwStyle=WS_OVERLAPPEDWINDOW;
}
RECT winRect;
winRect.left=0;
winRect.right=800;
winRect.top=0;
winRect.bottom=600;
AdjustWindowRectEx(&winRect, dwStyle,FALSE,dwExStyle);//依据所需客户矩形大小,计算需要的窗口矩形的大小。
// 计算出的窗口矩形随后可以传送给CreateWindowEx函数,
//用于创建一个客户区所需大小的窗口。
}
HWND hwnd=CreateWindowEx(dwExStyle,"HelloWin","HelloWin",
dwStyle|WS_CLIPSIBLINGS|WS_CLIPCHILDREN, //WS_CLIPSIBLINGS|WS_CLIPCHILDREN参考后面的参考资料
0,0,800,600,
NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,iCmdShow);
UpdateWindow(hwnd);
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
if(msg.message==WM_CLOSE)
{
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
PAINTSTRUCT ps;
switch(msg)
{
case WM_CREATE:
return 0;
case WM_PAINT:
{
HDC hdc=BeginPaint(hWnd,&ps);
EndPaint(hWnd,&ps);
return 0;
}
case WM_KEYDOWN:
switch(wParam)
{
case VK_ESCAPE:
PostMessage(hWnd,WM_CLOSE,0,0);
break;
}
return 0;
case WM_CLOSE:
DestroyWindow(hWnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd,msg,wParam,lParam);
}
13、最后主要的参考资料是《Beginning OpenGL Game Programming》以及Nehe的OpenGL中文教程
其他一些解读资料,来源博客,百科(好吧,不能加链接,只能标题,大家感兴趣可以搜一搜,我已深深地沉醉在不快乐痛苦的边缘了~):
WM_CLOSE WM_DESTROY WM_QUIT 区别
窗体的扩展样式和其值
关于DEVMODE的数据结构--显示设备的属性
很多框架在消息循环是喜欢用PeekMessage,所以,如何区别
WS_CLIPCHILDREN和WS_CLIPSIBLINGS的理解(不知道从哪转载的,图片挂了,但是点击右键-属性,把图片的地址贴到地址栏可以显示)
AdjustWindowRectEX函数 百科
14、最后申明下,版权的问题,如果您认为我侵犯了您或者其他人的著作权一定要跟我说呀~