OpenGL + Win32 SDK 开发框架的搭建(C语言版)
项目介绍:
近来公司需要建立一个OpenGL的开发环境,我需要设计一个框架,以便不熟悉Win32 API的用户能够直接”书写 glXXX代码”。
卖点:
1. 只使用C语言。
2. 只使用Win32 API和操作系统自带的动态库,决不使用第三方的库。
3. 支持多窗口管理。
开始摆 了~~~~~~~
=====================================================================================
设计思路:
GLWinApp.h 框架的头文件。
GLWinApp.c 实现了框架。
Example.c 一个测试例子。
先来按照“测试驱动开发模式”来说明我的Example.c如何使用我这个库。
#include "GLWinApp.h"
GLMY_WINDOW *gpWnd1, *gpWnd2, *gpWnd3;
/** 应用程序启动后的钩子 */
void myPreHook()
{
RECT rect;
GetClientRect(GetDesktopWindow(), &rect);
gpWnd1 = glmyCreateWindow(TEXT("test0"), TRUE, rect, 32);
rect.top = 0;
rect.left = 0;
rect.right = 300;
rect.bottom = 300;
gpWnd2 = glmyCreateWindow(TEXT("test1"), FALSE, rect, 32);
rect.left = 350;
rect.top = 0;
rect.right = 650;
rect.bottom = 300;
gpWnd3 = glmyCreateWindow(TEXT("test2"), FALSE, rect, 32);
glmyTimer(gpWnd1, 0, 10, TRUE); /**< 设定定时器0 */
glmyTimer(gpWnd2, 0, 10, TRUE); /**< 设定定时器0 */
glmyRefreshRate(1); /**< 设置刷新频率 */
return;
}
/** 应用程序结束前的钩子 */
void myPostHook()
{
}
/** 窗口尺寸改变后的钩子 */
void myResizeHook(GLMY_WINDOW * pWnd)
{
glViewport(0, 0, pWnd->nWidth, pWnd->nHeight); /**< 设置视口 */
glMatrixMode(GL_PROJECTION); /**< 重置投影矩阵 */
glLoadIdentity();
//gluPerspective(45.0f, (float)pWnd->nWidth / (float)pWnd->nHeight, 0, 1000.0f); /**< 使用对称透视视景体 */
gluOrtho2D(0, pWnd->nWidth, pWnd->nHeight, 0); /**< 使用平面坐标 */
glMatrixMode(GL_MODELVIEW); /**< 重置模型视点矩阵 */
glLoadIdentity();
}
/** 绘制的钩子 */
void myDrawHook(GLMY_WINDOW * pWnd)
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1, 0, 0);
glRecti(0, 0, 100, 100);
if (pWnd == gpWnd1)
{
//Draw wind1
}
if (pWnd == gpWnd2)
{
//Draw wind2
}
}
/** 定时器的钩子 */
void myTimerHook(GLMY_WINDOW * pWnd, unsigned char nIndex)
{
if (pWnd == gpWnd1 && nIndex == 0)
{
//Do something
}
if (pWnd == gpWnd2 && nIndex == 0)
{
//Do something
}
}
/** 键盘触发 */
void myKeyboardHook(GLMY_WINDOW * pWnd, GLMY_KEYBOARD_KEY key)
{
}
/** 鼠标触发 */
void myMouseHook(GLMY_WINDOW * pWnd, GLMY_MOUSE_BUTTON button, GLMY_MOUSE_ACTION action)
{
}
/** GLMY程序入口,给用户设置Hook的机会(必须由客户端实现) */
void GLMYENTRY glmyEntry()
{
glmySetPreHook(myPreHook);
glmySetPostHook(myPostHook);
glmySetResizeHook(myResizeHook);
glmySetDrawHook(myDrawHook);
glmySetTimerHook(myTimerHook);
glmySetKeyboardHook(myKeyboardHook);
glmySetMouseHook(myMouseHook);
}
=====================================================================================
框架介绍:
直接上代码吧:
先来头文件GLWinApp.h
/************************************************************************/
/*
建立OpenGL Windows应用程序的通用框架(C语言版)
特点:
1. 仅使用Windows系统自带的SDK。
2. 使用C语言,不使用C++语言。
3. 使用glmy前缀,表示自定义的库
设计说明:
1. 配置文件使用说明:
修改GLWinAppConfig.h文件,使用自定义的配置信息。
//可配置项说明
GLMY_PRE_HOOK glmyPreHook
GLMY_POST_HOOK glmyPostHook
GLMY_KEYBOARD_HOOK glmyKeyboardHook
GLMY_MOUSE_HOOK glmyMouseHook
GLMY_DRAW_HOOK glmyDrawHook
GLMY_TIMER_HOOK glmyTimerHook
2. 接口说明:
新建一个或多个*.c的文件,写相应的函数。
//HOOK函数说明
glmyPreHook 程序启动时的Hook,可以创建窗口
glmyPostHook 程序结束时的Hook,可以销毁窗口
glmyKeyboardHook 键盘响应Hook
glmyMouseHook 鼠标响应Hook
glmyDrawHook 绘图需求Hook
glmyTimerHook 定时器响应Hook
//可调用函数说明
glmyCreateWindow 创建一个GL窗口
glmyDestroyWindow 销毁一个GL窗口
*/
/************************************************************************/
#ifndef _GLWINAPP_H_
#define _GLWINAPP_H_
#ifdef __cplusplus
extern "C" {
#endif
/************************************************************************/
/* 宏定义,预处理定义,头文件 声明 */
/************************************************************************/
#define GLMYAPI __declspec(dllexport) /* 是否导出? */
#define GLMYENTRY __stdcall /* 约定程序调用方式 */
#pragma comment (lib, "opengl32.lib") /* link Microsoft OpenGL lib */
#pragma comment (lib, "glu32.lib") /* link OpenGL Utility lib */
#define _CRT_SECURE_NO_WARNINGS /**< c4996 warning, _stprintf() */
#define _CRT_NON_CONFORMING_SWPRINTFS
#include <windows.h>
#include <tchar.h>
#include <assert.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include "GLWinAppConfig.h" /**< 编译配置文件 */
/************************************************************************/
/* 数据结构定义 */
/************************************************************************/
/** 鼠标按键 */
typedef enum
{
GLMY_MOUSE_LEFT_BUTTON = 0x0001,
GLMY_MOUSE_RIGHT_BUTTON = 0x0002,
GLMY_MOUSE_MIDDLE_BUTTON = 0x0004
} GLMY_MOUSE_BUTTON;
/** 鼠标动作 */
typedef enum
{
GLMY_MOUSE_DOWN = 0x0001,
GLMY_MOUSE_UP = 0x0002,
GLMY_MOUSE_MOVE = 0x0004
} GLMY_MOUSE_ACTION;
/** 键盘按键 */
typedef enum
{
GLMY_KEY_ESC,
GLMY_KEY_F1,
GLMY_KEY_F2,
GLMY_KEY_F3,
GLMY_KEY_LEFT,
GLMY_KEY_RIGHT,
GLMY_KEY_UP,
GLMY_KEY_DOWN,
GLMY_KEY_SPACE
} GLMY_KEYBOARD_KEY;
/** 结构 GLMY程序 */
typedef struct _GLMY_WINDOW
{
HWND hWnd;
HDC hDC;
HGLRC hRC;
BOOL bFullScreen;
unsigned int nWidth;
unsigned int nHeight;
unsigned int nBitsPerPixel;
struct _GLMY_WINDOW *pNext;
} GLMY_WINDOW;
/************************************************************************/
/* 接口声明 */
/************************************************************************/
/**** 窗口管理函数 ****/
/** 建立窗口 */
GLMY_WINDOW * GLMYENTRY glmyCreateWindow(TCHAR * title, BOOL bFullscreen,
RECT rect, int nBitsPerPixel);
/** 销毁窗口 */
void GLMYENTRY glmyDestroyWindow(GLMY_WINDOW * pWnd);
/** 设置全屏 */
void GLMYENTRY glmyFullscreen(BOOL bEnable);
/** 设置刷新帧率 */
void GLMYENTRY glmyRefreshRate(unsigned int nMs);
/** 更改窗口大小 */
void GLMYENTRY glmyResize(GLMY_WINDOW * pWnd, int nWidth, int nHeight);
/** 设置定时器 */
void GLMYENTRY glmyTimer(GLMY_WINDOW * pWnd, unsigned char nIndex, unsigned int nMs, BOOL bEnable);
/**** 注册回调函数 ****/
/** GLMY程序入口,给用户设置Hook的机会(必须由客户端实现) */
void GLMYENTRY glmyEntry();
/** 设置 程序启动后的Hook */
void GLMYENTRY glmySetPreHook( void (*pFunc)());
/** 设置 程序结束前的Hook */
void GLMYENTRY glmySetPostHook(void (*pFunc)());
/** 设置 窗口尺寸改变后的Hook */
void GLMYENTRY glmySetResizeHook(void (*pFunc)(GLMY_WINDOW * pWnd));
/** 设置 绘制的Hook */
void GLMYENTRY glmySetDrawHook(void (*pFunc)());
/** 设置 定时器触发的Hook */
void GLMYENTRY glmySetTimerHook(void (*pFunc)(GLMY_WINDOW * pWnd, unsigned char nIndex));
/** 设置 键盘触发的Hook */
void GLMYENTRY glmySetKeyboardHook(void (*pFunc)(GLMY_WINDOW * pWnd, GLMY_KEYBOARD_KEY key));
/** 设置 鼠标触发的Hook */
void GLMYENTRY glmySetMouseHook(void (*pFunc)(GLMY_WINDOW * pWnd, GLMY_MOUSE_BUTTON button, GLMY_MOUSE_ACTION action));
#ifdef __cplusplus
}
#endif
/*** END OF FILE ***/
#endif /* _GLWINAPP_H_ */
下面介绍GLWinApp.c的主要内容
#include "GLWinApp.h"
/************************************************************************/
/* 私有成员 */
/************************************************************************/
/** 常量 */
#define GLMY_WINDOW_MAX 99 /**< 最大的窗口数量 */
/** 字段 */
static HINSTANCE m_hInstance = NULL; /**< 程序实例 */
static TCHAR const * m_sAppName = TEXT("GLMYApplication"); /**< 窗口类的名称 */
static GLMY_WINDOW * m_pWndListHead = NULL; /**< GL窗口对象链表 */
static unsigned int m_nWndListCount = 0; /**< GL窗口对象链表的当前长度 */
static unsigned int m_nRefreshRate = 35; /**< 刷新帧率(ms) */
/** Hook函数指针 */
static void ( * m_PreHook )( void ) = 0; /**< 程序启动后的Hook */
static void ( * m_PostHook )( void ) = 0; /**< 程序退出前的Hook */
static void ( * m_ResizeHook)( GLMY_WINDOW * pWnd ) = 0; /**< 窗口尺寸改变后的Hook */
static void ( * m_DrawHook )( GLMY_WINDOW * pWnd ) = 0; /**< 绘制的Hook */
static void ( * m_TimerHook )( GLMY_WINDOW * pWnd,
unsigned char nIndex ) = 0; /**< 定时器触发的Hook */
static void ( * m_KeyboardHook )( GLMY_WINDOW * pWnd,
GLMY_KEYBOARD_KEY key ) = 0; /**< 键盘触发的Hook */
static void ( * m_MouseHook )( GLMY_WINDOW * pWnd,
GLMY_MOUSE_BUTTON button,
GLMY_MOUSE_ACTION action ) = 0; /**< 鼠标触发的Hook */
/************************************************************************/
/* 内部函数声明 */
/************************************************************************/
/** 注册窗口类 */
static BOOL glmy_RegWndClass(HINSTANCE hInstance, WNDPROC wndProc);
/** 消息处理函数 */
static LRESULT CALLBACK glmy_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
/** 设置屏幕模式 */
static BOOL glmyChangeScreenMode(BOOL bFullscreen, int nWidth, int nHeight, int nBitsPerPixel);
/** 启动OpenGL渲染环境 */
static void glmyEnableGL(GLMY_WINDOW * pApp);
/** 停止OpenGL渲染环境 */
static void glmyDisableGL(GLMY_WINDOW *pApp);
/** 窗口链表 - 增加 */
static BOOL glmy_WndList_Add(GLMY_WINDOW * pWnd);
/** 窗口链表 - 删除 */
static BOOL glmy_WndList_Delete(GLMY_WINDOW * pWnd);
/** 窗口链表 - 查找 */
static GLMY_WINDOW * glmy_WndList_Search(HWND hWnd);
/** 窗口链表 - 反向销毁所有的窗口对象 */
static void glmy_WndList_ReverseFreeAll();
/************************************************************************/
/* 内部函数实现 */
/************************************************************************/
/** Windows程序入口 */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg; /**< 定义一个消息 */
BOOL bQuit = FALSE; /**< 是否退出消息循环 */
m_hInstance = hInstance; /**< 记录程序实例 */
if (!glmy_RegWndClass(hInstance, glmy_WndProc)) /**< 注册一个窗口类 */
{
return -1;
}
glmyChangeScreenMode(FALSE, 0, 0, 0); /**< 强行切换一次显示模式,在以下的创建过程中,如果是全屏,才会修改一次显示模式 */
#if GLMY_HOOK
glmyEntry();
#endif
#if GLMY_PRE_HOOK
if (m_PreHook != NULL) /**< 启动Hook */
{
m_PreHook();
}
#endif
/** 传统的消息循环 */
/*
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
*/
/** 不使用定时器的消息循环 */
while (!bQuit) /**< 消息循环 */
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) /**< 检查消息 */
{
if (msg.message == WM_QUIT) /**< 检查退出消息 */
{
bQuit = TRUE;
}
else
{
TranslateMessage(&msg); /**< 翻译消息 */
DispatchMessage(&msg); /**< 分发消息 */
}
}
else
{
GLMY_WINDOW * pNextWnd = m_pWndListHead;
int nWndCnt = m_nWndListCount > 0 ? m_nWndListCount : 1;
int nSleepMs = m_nRefreshRate / nWndCnt; /**< 计算休眠时间 */
nSleepMs = nSleepMs < 1 ? 1 : nSleepMs; /**< 防止为0 */
while (pNextWnd != NULL)
{
#if GLMY_DRAW_HOOK
if (m_DrawHook != NULL)
{
wglMakeCurrent(pNextWnd->hDC, pNextWnd->hRC);
m_DrawHook(pNextWnd);
SwapBuffers(pNextWnd->hDC); /**< 交换到缓冲区 */
wglMakeCurrent(NULL, NULL);
}
#endif
Sleep(nSleepMs);
pNextWnd = pNextWnd->pNext;
}
}
}
#if GLMY_POST_HOOK
if (m_PostHook != NULL) /**< 结束Hook */
{
m_PostHook();
}
#endif
glmy_WndList_ReverseFreeAll(); /**< 反向清空所有的窗体对象 */
return msg.wParam;
}
/** 注册窗口类 */
static BOOL glmy_RegWndClass(HINSTANCE hInstance, WNDPROC wndProc)
{
WNDCLASSEX wcex;
/* 注册窗口类 */
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wcex.lpfnWndProc = wndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = m_sAppName;
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wcex))
{
return FALSE;
}
return TRUE;
}
/** 消息处理函数 */
static LRESULT CALLBACK glmy_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
GLMY_WINDOW* pWnd = NULL;
GLMY_KEYBOARD_KEY key = GLMY_KEY_SPACE; /**< 键盘按键 */
pWnd = glmy_WndList_Search(hWnd);
switch(uMsg)
{
case WM_CREATE:
break;
case WM_CLOSE:
if (pWnd != NULL)
{
glmy_WndList_Delete(pWnd); /**< 关闭当前窗口,并清除相关资源 */
}
if (m_pWndListHead == NULL) /**< 如果窗口列表为空,则退出程序 */
{
PostQuitMessage(0);
}
break;
case WM_SIZE:
if (pWnd != NULL)
{
#if GLMY_RESIZE_HOOK
pWnd->nWidth = LOWORD(lParam); /**< 记录宽度和高度 */
pWnd->nHeight = HIWORD(lParam);
pWnd->nHeight = pWnd->nHeight > 1 ? pWnd->nHeight : 1; /**< 防止为0 */
wglMakeCurrent(pWnd->hDC, pWnd->hRC); /**< 选中DC和RC */
if (m_ResizeHook != NULL)
{
m_ResizeHook(pWnd, pWnd->nWidth, pWnd->nHeight); /**< 调用Hook */
}
wglMakeCurrent(NULL, NULL); /**< 释放DC和RC */
#endif
}
break;
case WM_KEYDOWN:
switch(wParam)
{
case VK_F1:
key = GLMY_KEY_F1;
break;
case VK_F2:
key = GLMY_KEY_F2;
break;
case VK_F3:
key = GLMY_KEY_F3;
break;
case VK_ESCAPE:
PostQuitMessage(0);
break;
}
#if GLMY_KEYBOARD_HOOK
if (m_KeyboardHook != NULL)
{
m_KeyboardHook(pWnd, key);
}
#endif
break;
case WM_LBUTTONDOWN:
#if GLMY_KEYBOARD_HOOK
if (m_MouseHook != NULL)
{
m_MouseHook(pWnd, GLMY_MOUSE_LEFT_BUTTON, GLMY_MOUSE_DOWN);
}
#endif
break;
case WM_LBUTTONUP:
#if GLMY_KEYBOARD_HOOK
if (m_MouseHook != NULL)
{
m_MouseHook(pWnd, GLMY_MOUSE_LEFT_BUTTON, GLMY_MOUSE_UP);
}
#endif
break;
case WM_RBUTTONDOWN:
#if GLMY_KEYBOARD_HOOK
if (m_MouseHook != NULL)
{
m_MouseHook(pWnd, GLMY_MOUSE_RIGHT_BUTTON, GLMY_MOUSE_DOWN);
}
#endif
break;
case WM_RBUTTONUP:
#if GLMY_KEYBOARD_HOOK
if (m_MouseHook != NULL)
{
m_MouseHook(pWnd, GLMY_MOUSE_RIGHT_BUTTON, GLMY_MOUSE_UP);
}
#endif
break;
case WM_MBUTTONDOWN:
#if GLMY_KEYBOARD_HOOK
if (m_MouseHook != NULL)
{
m_MouseHook(pWnd, GLMY_MOUSE_MIDDLE_BUTTON, GLMY_MOUSE_DOWN);
}
#endif
break;
case WM_MBUTTONUP:
#if GLMY_KEYBOARD_HOOK
if (m_MouseHook != NULL)
{
m_MouseHook(pWnd, GLMY_MOUSE_MIDDLE_BUTTON, GLMY_MOUSE_UP);
}
#endif
break;
case WM_MOUSEMOVE:
#if GLMY_KEYBOARD_HOOK
if (m_MouseHook != NULL)
{
m_MouseHook(pWnd, GLMY_MOUSE_LEFT_BUTTON, GLMY_MOUSE_MOVE);
}
#endif
break;
case WM_TIMER:
#if GLMY_TIMER_HOOK
if (m_TimerHook != NULL)
{
m_TimerHook(pWnd, (unsigned int)wParam);
}
#endif
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
/** 设置屏幕模式 */
static BOOL glmyChangeScreenMode(BOOL bFullscreen, int nWidth, int nHeight, int nBitsPerPixel)
{
if (!bFullscreen)
{
ChangeDisplaySettings(NULL, 0); /**< 回到桌面 */
}
else
{
DEVMODE dmScreenSettings; /**< 设备模式 */
memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
dmScreenSettings.dmSize = sizeof(DEVMODE);
dmScreenSettings.dmPelsWidth = nWidth; /**< 屏幕宽度 */
dmScreenSettings.dmPelsHeight = nHeight; /**< 屏幕高度 */
dmScreenSettings.dmBitsPerPel = nBitsPerPixel; /**< 色彩深度 */
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
// 尝试设置显示模式并返回结果。注: CDS_FULLSCREEN 移去了状态栏
if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
return FALSE;
}
}
return TRUE;
}
/** 启动OpenGL渲染环境 */
static void glmyEnableGL(GLMY_WINDOW * pApp)
{
PIXELFORMATDESCRIPTOR pfd = /**< 像素格式设置 */
{
sizeof(PIXELFORMATDESCRIPTOR),
1, /**< 版本号 */
PFD_DRAW_TO_WINDOW /**< 格式支持窗口 */
| PFD_SUPPORT_OPENGL /**< 格式必须支持OpenGL */
| PFD_DOUBLEBUFFER /**< 必须支持双缓冲 */
| PFD_TYPE_RGBA, /**< RGBA颜色格式 */
pApp->nBitsPerPixel, /**< 选定色彩深度 */
0, 0, 0, 0, 0, 0, /**< 忽略的色彩位 */
0, /**< 无Alpha缓存 */
0, /**< 忽略Shift Bit */
0, /**< 无累加缓存 */
0, 0, 0, 0, /**< 忽略聚集位 */
16, /**< 16位 Z-缓存(深度缓存) */
0, /**< 无模板缓存 */
0, /**< 无辅助缓存 */
PFD_MAIN_PLANE, /**< 主绘图层 */
0, /**< 不使用重叠层 */
0, 0, 0 /**< 忽略层遮罩 */
};
int nPixelFormatIndex; /**< 像素格式序号 */
pApp->hDC = GetDC(pApp->hWnd); /**< 获取HDC */
nPixelFormatIndex = ChoosePixelFormat(pApp->hDC, &pfd); /**< 取得索引 */
SetPixelFormat(pApp->hDC, nPixelFormatIndex, &pfd); /**< 设置索引 */
pApp->hRC = wglCreateContext(pApp->hDC); /**< 创建RC */
wglMakeCurrent(pApp->hDC, pApp->hRC); /**< 测试选中,取消 */
wglMakeCurrent(NULL, NULL);
}
/** 停止OpenGL渲染环境 */
static void glmyDisableGL(GLMY_WINDOW *pApp)
{
if (pApp->hRC)
{
wglMakeCurrent(NULL, NULL); /**< 释放DC和RC */
wglDeleteContext(pApp->hRC); /**< 释放RC */
pApp->hRC = NULL;
}
if (pApp->hDC)
{
ReleaseDC(pApp->hWnd, pApp->hDC); /**< 释放DC */
pApp->hDC = NULL;
}
}
/************************************************************************/
/* 接口函数实现 */
/************************************************************************/
/** 建立GL窗口 */
GLMY_WINDOW * GLMYENTRY glmyCreateWindow(TCHAR * title, BOOL bFullscreen,
RECT rect, int nBitsPerPixel)
{
GLMY_WINDOW *pWnd;
BOOL bQuit = FALSE;
DWORD dwStyle;
DWORD dwExStyle;
pWnd = calloc(1, sizeof(GLMY_WINDOW)); /**< 在堆上分配内存,一定要注意释放 */
pWnd->bFullScreen = bFullscreen;
pWnd->nWidth = rect.right - rect.left;
pWnd->nHeight = rect.bottom - rect.top;
pWnd->nBitsPerPixel = nBitsPerPixel;
if (pWnd->bFullScreen) /**< 如果是全屏,则要切换显示模式 */
{
glmyChangeScreenMode(bFullscreen,
rect.right - rect.left,
rect.bottom - rect.top,
nBitsPerPixel);
}
/** 创建窗口 */
if (bFullscreen)
{
dwStyle = WS_POPUP;
dwExStyle = WS_EX_APPWINDOW;
//ShowCursor(FALSE);
}
else
{
dwStyle = WS_OVERLAPPEDWINDOW;
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
//ShowCursor(TRUE);
}
dwStyle |= WS_CLIPCHILDREN /**< 排除子窗口 */
| WS_CLIPSIBLINGS; /**< 排除同类窗口 */
pWnd->hWnd = CreateWindowEx(dwExStyle,
m_sAppName,
title,
dwStyle,
rect.left, /**< start x */
rect.top, /**< start y */
rect.right - rect.left, /**< width */
rect.bottom - rect.top, /**< height */
NULL, /**< parent window handle */
NULL, /**< window menu handle */
m_hInstance, /**< program instance handle */
NULL); /**< creation parameters */
if (pWnd->hWnd == NULL) /**< 如果创建失败? */
{
free(pWnd);
return FALSE;
}
if (!glmy_WndList_Add(pWnd)) /**< 增加到链表。如果失败,则可能是超出了容量或内存分配失败 */
{
free(pWnd);
return NULL;
}
glmyEnableGL(pWnd); /**< 启用OpenGL渲染环境 */
glmyResize(pWnd, pWnd->nWidth, pWnd->nHeight); /**< 手动触发Resize事件,使之能够调整GL环境 */
ShowWindow(pWnd->hWnd, SW_SHOW); /**< 显示窗口 */
SetForegroundWindow(pWnd->hWnd);
SetFocus(pWnd->hWnd);
return pWnd;
}
/** 删除GL窗口 */
void GLMYENTRY glmyDestroyWindow(GLMY_WINDOW * pWnd)
{
assert(pWnd != NULL); /**< 调试时不允许错误,限制用户必须维护数据的正确 */
if (pWnd == NULL) /**< 防止用户传入空参数 */
{
return;
}
if (pWnd->bFullScreen) /**< 如果是全屏,则要切换回桌面 */
{
glmyChangeScreenMode(FALSE, 0, 0, 0);
}
glmyDisableGL(pWnd); /**< 先禁止OpenGL环境 */
if (pWnd->hWnd != NULL) /**< 销毁窗口 */
{
DestroyWindow(pWnd->hWnd);
//SendMessage(pWnd->hWnd, WM_CLOSE, 0, 0);
pWnd->hWnd = NULL;
}
}
/** 设置全屏 */
void GLMYENTRY glmyFullscreen(BOOL bEnable)
{
}
/** 设置刷新帧率 */
void GLMYENTRY glmyRefreshRate(unsigned int nMs)
{
int nRefreshRateMin = 1; /**< 最快1ms刷新一次 */
int nRefreshRateMax = 10000; /**< 最慢10s刷新一次 */
nMs = nMs < nRefreshRateMin ? nRefreshRateMin : nMs;
nMs = nMs > nRefreshRateMax ? nRefreshRateMax : nMs;
m_nRefreshRate = nMs;
}
/** 更改窗口大小 */
void GLMYENTRY glmyResize(GLMY_WINDOW * pWnd, int nWidth, int nHeight)
{
assert(pWnd != NULL);
assert(pWnd->hWnd != NULL);
assert(pWnd->hDC != NULL);
assert(pWnd->hRC != NULL);
SendMessage(pWnd->hWnd, WM_SIZE, 0, MAKELONG(nWidth, nHeight)); /**< 发送WM_SIZE消息 */
}
/** 设置定时器 */
void GLMYENTRY glmyTimer(GLMY_WINDOW * pWnd, unsigned char nIndex, unsigned int nMs, BOOL bEnable)
{
if (pWnd == NULL || pWnd->hWnd == NULL)
{
return;
}
nMs = nMs < 10 ? 10 : nMs; /**< 定时器精度最大为10ms */
if (bEnable)
{
SetTimer(pWnd->hWnd, nIndex, nMs, NULL);
}
else
{
KillTimer(pWnd->hWnd, nIndex);
}
}
/** 设置 程序启动后的Hook */
void GLMYENTRY glmySetPreHook(void (*pFunc)())
{
m_PreHook = pFunc;
}
/** 设置 程序结束前的Hook */
void GLMYENTRY glmySetPostHook(void (*pFunc)())
{
m_PostHook = pFunc;
}
/** 设置 窗口尺寸改变后的Hook */
void GLMYENTRY glmySetResizeHook(void (*pFunc)(GLMY_WINDOW * pWnd))
{
m_ResizeHook = pFunc;
}
/** 设置 绘制的Hook */
void GLMYENTRY glmySetDrawHook(void (*pFunc)())
{
m_DrawHook = pFunc;
}
/** 设置 定时器触发的Hook */
void GLMYENTRY glmySetTimerHook(void (*pFunc)(GLMY_WINDOW * pWnd, unsigned char nIndex))
{
m_TimerHook = pFunc;
}
/** 设置 键盘触发的Hook */
void GLMYENTRY glmySetKeyboardHook(void (*pFunc)(GLMY_WINDOW * pWnd, GLMY_KEYBOARD_KEY key))
{
m_KeyboardHook = pFunc;
}
/** 设置 鼠标触发的Hook */
void GLMYENTRY glmySetMouseHook(void (*pFunc)(GLMY_WINDOW * pWnd, GLMY_MOUSE_BUTTON button, GLMY_MOUSE_ACTION action))
{
m_MouseHook = pFunc;
}
下面补充WndList的链表操作。
/** 窗口链表 - 增加 */
static BOOL glmy_WndList_Add( GLMY_WINDOW * pWnd )
{
int count = 0;
GLMY_WINDOW * pPrev = m_pWndListHead, * pNode;
assert(pWnd != NULL); //传入元素必不为空
if (m_pWndListHead == NULL) //如果链表为空
{
m_pWndListHead = pWnd;
m_nWndListCount = 1; /**< 数量为1 */
return TRUE;
}
pNode = pPrev;
while (pNode != NULL) //找到第一个空节点
{
pPrev = pNode;
pNode = pNode->pNext;
count++;
if (count >= GLMY_WINDOW_MAX) //超出链表容量
{
return FALSE;
}
}
pPrev->pNext = pWnd;
m_nWndListCount++; /**< 数量+1 */
return TRUE;
}
/** 窗口链表 - 删除 */
static BOOL glmy_WndList_Delete( GLMY_WINDOW * pWnd )
{
GLMY_WINDOW * pPrev = m_pWndListHead, * pNode = NULL;
assert(pPrev != NULL); //链表必不为空
assert(pWnd != NULL); //传入元素必不为空
if (pWnd == m_pWndListHead) //如果就是头结点
{
pNode = pWnd->pNext; //先取得后一个元素
glmyDestroyWindow(pWnd); /**< 释放窗体资源 */
free(pWnd); /**< 释放GL窗体节点 */
m_pWndListHead = pNode; /**< 修改表头 */
m_nWndListCount = 0; /**< 数量为0 */
return TRUE;
}
pNode = pPrev;
while(pNode != NULL)
{
pPrev = pNode;
pNode = pNode->pNext;
if (pNode == pWnd) //找到元素
{
pPrev->pNext = pNode->pNext; /**< 指向下下个节点 */
m_nWndListCount--; /**< 数量-1 */
glmyDestroyWindow(pWnd); /**< 释放窗体资源 */
free(pWnd); /**< 释放GL窗体节点 */
return TRUE;
}
}
return FALSE;
}
/** 窗口链表 - 查找 */
static GLMY_WINDOW * glmy_WndList_Search(HWND hWnd)
{
GLMY_WINDOW * pNode = NULL, *pHead;
pHead = m_pWndListHead;
while (pHead != NULL)
{
if (pHead->hWnd == hWnd)
{
pNode = pHead;
break;
}
pHead = pHead->pNext;
}
//assert(pWnd != NULL); /**< !此处不可避免的会失败,当CreateWindows()运行后自动发送WM_SIZE事件,但是还没有机会将新的hWND有关的GL_Wnd存储下来。
return pNode;
}
/** 窗口链表 - 反向销毁所有的窗口对象 */
static void glmy_WndList_ReverseFreeAll()
{
GLMY_WINDOW * pPrev, * pNode;
while (TRUE)
{
pPrev = m_pWndListHead;
if (pPrev == NULL) //表头为空
{
return;
}
pNode = pPrev->pNext;
if (pNode == NULL) //只有一个元素
{
glmyDestroyWindow(pPrev);
free(pPrev);
m_pWndListHead = NULL;
m_nWndListCount = 0; /**< 数量为0 */
return;
}
while (pNode != NULL) //元素数量>=2。找到末尾节点。
{
if (pNode->pNext == NULL)
{
break;
}
else
{
pPrev = pNode;
pNode = pNode->pNext;
}
}
glmyDestroyWindow(pPrev->pNext);
free(pPrev->pNext); //销毁刚找到的末尾节点,使之数量减1
pPrev->pNext = NULL;
m_nWndListCount--; /**< 数量-1 */
}
assert(m_nWndListCount == 0); /**< 数量必为0 */
}
=====================================================================================
(华丽的结束线)
后记:当然,可能还有些许错误,也还没有做任何优化,期待我的库能够运行下去。欢迎各位指正。
版权声明:本文为openlibcn原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。