項目介紹:
近來公司需要建立一個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 */
}
=====================================================================================
(華麗的結束線)
後記:當然,可能還有些許錯誤,也還沒有做任何優化,期待我的庫能夠運行下去。歡迎各位指正。