Skip to content

Instantly share code, notes, and snippets.

@gszauer
Created June 6, 2013 00:32
Show Gist options
  • Save gszauer/5718451 to your computer and use it in GitHub Desktop.
Save gszauer/5718451 to your computer and use it in GitHub Desktop.
#define WIN32_LEAN_AND_MEAN
#define WIN32_EXTRA_LEAN
#include <windows.h>
#include <process.h>
#include <stdlib.h>
#include <gl/gl.h>
#include <cmath>
#pragma comment(lib, "opengl32.lib")
#define OGL_NEAR float(0.1f)
#define OGL_FAR float(1000.0f)
#define START_FULLSCREEN false
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#define TARGET_FPS 60
#define WND_CLASSNAME "GLWindow"
#define WND_TITLE "OpenGL Enabled Window (Advanced)"
#define THROTTLE_FPS 1
#define ENABLE_RESIZE 1
#define MULTITHREADED 1
#define USEOPENGL3X 1
#define FORCE_FORWARDCOMPATABLE 0
HWND hwnd;
HINSTANCE hinstance;
HGLRC hglrc;
unsigned int glMajor;
unsigned int glMinor;
////////////////////////////////////////////////////////////////////
// Game Hook
void Initialize() {}
void Update() {}
void Render() {
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslatef(0.0f,0.0f,-6.0f);
glBegin(GL_TRIANGLES);
glColor3f(1.0f,0.0f,0.0f);
glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(0.0f,1.0f,0.0f);
glVertex3f(-1.0f,-1.0f, 1.0f);
glColor3f(0.0f,0.0f,1.0f);
glVertex3f( 1.0f,-1.0f, 1.0f);
glEnd();
glPopMatrix();
}
void Shutdown() {}
void Resize(int width, int height) { }
////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
bool CheckIfAlreadyRunning();
void OpenGLUnbindContext(HWND hwnd, HDC hdc, HGLRC hglrc);
void OpenGLResetProjection();
HGLRC OpenGLBindContext(HDC hdc);
void ToggleFullscreen(bool init = false, bool exit = false);
void SetDisplayMode(int width, int height, int bpp, int refreshRate);
#if MULTITHREADED
unsigned int __stdcall GameThread(void *args);
#endif
#if USEOPENGL3X
typedef HGLRC (APIENTRY * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext, const int *attribList);
#endif
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
if (!hPrevInstance)
if (CheckIfAlreadyRunning())
return FALSE;
#if THROTTLE_FPS
const int SKIP_TICKS = 1000 / TARGET_FPS;
DWORD next_game_tick = GetTickCount();
int sleep_time = 0;
#endif
#if MULTITHREADED
const unsigned int signalstart = 0;
const unsigned int signalstop = 1;
unsigned int gamethreadid;
volatile HANDLE hgamesignals[2];
HANDLE hgamethread;
hgamesignals[signalstart] = CreateMutex(NULL, TRUE, NULL);
hgamesignals[signalstop] = CreateMutex(NULL, TRUE, NULL);
#endif
MSG msg; HDC hdc;
hinstance = hInstance;
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wndclass.lpszMenuName = 0;
wndclass.lpszClassName = WND_CLASSNAME;
RegisterClassEx(&wndclass);
RECT windowRect;
SetRect(&windowRect, (GetSystemMetrics(SM_CXSCREEN) / 2) - (WINDOW_WIDTH / 2), (GetSystemMetrics(SM_CYSCREEN) / 2) - (WINDOW_HEIGHT / 2), (GetSystemMetrics(SM_CXSCREEN) / 2) + (WINDOW_WIDTH / 2), (GetSystemMetrics(SM_CYSCREEN) / 2) + (WINDOW_HEIGHT / 2));
#if ENABLE_RESIZE
AdjustWindowRectEx(&windowRect, WS_VISIBLE | WS_OVERLAPPEDWINDOW, FALSE, 0);
hwnd = CreateWindowEx(0, WND_CLASSNAME, WND_TITLE, WS_VISIBLE | WS_OVERLAPPEDWINDOW, windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, NULL, NULL, hInstance, szCmdLine);
#else
AdjustWindowRectEx(&windowRect, WS_VISIBLE | (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX /*| WS_MAXIMIZEBOX*/), FALSE, 0);
hwnd = CreateWindowEx(0, WND_CLASSNAME, WND_TITLE, WS_VISIBLE | (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX /*| WS_MAXIMIZEBOX*/), windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, NULL, NULL, hInstance, szCmdLine);
#endif
hdc = GetDC(hwnd);
hglrc = OpenGLBindContext(hdc);
#if !MULTITHREADED
ToggleFullscreen(true, false);
Initialize();
#else
wglMakeCurrent(NULL, NULL);
hgamethread = (HANDLE)_beginthreadex(NULL, 0, GameThread, (void*)hgamesignals, 0, &gamethreadid);
#endif
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
#if !MULTITHREADED
OpenGLResetProjection();
#else
ReleaseMutex(hgamesignals[signalstart]);
#endif
while (true) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
#if !MULTITHREADED
#if THROTTLE_FPS
next_game_tick += SKIP_TICKS;
sleep_time = next_game_tick - GetTickCount();
if(sleep_time >= 0)
Sleep(sleep_time);
#endif
Update();
Render();
SwapBuffers(hdc);
#endif
}
#if !MULTITHREADED
Shutdown();
ToggleFullscreen(false, true);
#else
ReleaseMutex(hgamesignals[signalstop]);
WaitForSingleObject(hgamethread, INFINITE);
CloseHandle(hgamesignals[signalstart]);
CloseHandle(hgamesignals[signalstop]);
CloseHandle(hgamethread);
wglMakeCurrent(hdc, hglrc);
#endif
OpenGLUnbindContext(hwnd, hdc, hglrc);
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
static bool cursorVisible = true;
switch (iMsg) {
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case (WM_APP + 1):
if (!cursorVisible) {
ShowCursor(TRUE);
cursorVisible = true;
}
break;
case (WM_APP + 2):
if (cursorVisible) {
ShowCursor(FALSE);
cursorVisible = false;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
#if !MULTITHREADED
if (wParam == VK_RETURN) {
if ((HIWORD(lParam) & KF_ALTDOWN)) {
ToggleFullscreen();
}
} else if (wParam == VK_F4) {
PostQuitMessage(0);
}
#endif
break;
case WM_SIZE:
OpenGLResetProjection();
break;
case WM_SYSKEYUP:
case WM_SYSCHAR:
case WM_PAINT:
case WM_ERASEBKGND:
return 0;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
HGLRC OpenGLBindContext(HDC hdc) {
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 32;
pfd.cStencilBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
int pixelFormat = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, pixelFormat, &pfd);
HGLRC context = wglCreateContext(hdc);
wglMakeCurrent(hdc, context);
glMajor = 1;
glMinor = 1;
#if USEOPENGL3X
float glVersion = float(atof((const char*)glGetString(GL_VERSION)));
if (glVersion < 3.0f)
return context;
int attribs[] = {
/*WGL_CONTEXT_MAJOR_VERSION_ARB*/0x2091, 3,
/*WGL_CONTEXT_MINOR_VERSION_ARB*/0x2092, 1,
/*WGL_CONTEXT_FLAGS_ARB*/0x2094,
#if FORCE_FORWARDCOMPATABLE
/*WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB*/0x0002,
#else
0,
#endif
0, 0 };
glMajor = 3;
glMinor = 1;
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
HGLRC forwardContext = wglCreateContextAttribsARB(hdc, 0, attribs);
wglMakeCurrent(NULL, NULL);
wglDeleteContext(context);
wglMakeCurrent(hdc, forwardContext);
return forwardContext;
#else
return context;
#endif
}
void OpenGLUnbindContext(HWND hwnd, HDC hdc, HGLRC hglrc) {
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hglrc);
ReleaseDC(hwnd, hdc);
}
bool CheckIfAlreadyRunning(void) {
HWND hWnd = FindWindow(WND_CLASSNAME, WND_TITLE);
if (hWnd) {
if (IsIconic(hWnd))
ShowWindow(hWnd, SW_RESTORE);
SetForegroundWindow(hWnd);
return true;
}
return false;
}
void ToggleFullscreen(bool init, bool exit) {
static bool fullScreen = !START_FULLSCREEN;
static RECT windowRect = { 0 };
if (init) {
SetRect(&windowRect, (GetSystemMetrics(SM_CXSCREEN) / 2) - (WINDOW_WIDTH / 2), (GetSystemMetrics(SM_CYSCREEN) / 2) - (WINDOW_HEIGHT / 2), (GetSystemMetrics(SM_CXSCREEN) / 2) + (WINDOW_WIDTH / 2), (GetSystemMetrics(SM_CYSCREEN) / 2) + (WINDOW_HEIGHT / 2));
#if ENABLE_RESIZE
AdjustWindowRectEx(&windowRect, WS_VISIBLE | WS_OVERLAPPEDWINDOW, FALSE, 0);
#else
AdjustWindowRectEx(&windowRect, WS_VISIBLE | (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX /*| WS_MAXIMIZEBOX*/), FALSE, 0);
#endif
} else if (exit) {
if (fullScreen) {
SetDisplayMode(0, 0, 0, 0);
#if ENABLE_RESIZE
SetWindowLongPtr(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
#else
SetWindowLongPtr(hwnd, GWL_STYLE, (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX /*| WS_MAXIMIZEBOX*/));
#endif
SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
int iWindowWidth = windowRect.right - windowRect.left;
int iWindowHeight = windowRect.bottom - windowRect.top;
SetWindowPos(hwnd, 0, windowRect.left, windowRect.top, iWindowWidth, iWindowHeight, SWP_NOZORDER);
fullScreen = false;
}
return;
}
if(!fullScreen) {
GetWindowRect(hwnd, &windowRect);
HDC hdc = GetDC(hwnd);
SetDisplayMode(GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), GetDeviceCaps(hdc, BITSPIXEL), GetDeviceCaps(hdc, VREFRESH));
SetWindowLongPtr(hwnd, GWL_STYLE, WS_POPUP);
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
SetWindowPos(hwnd, 0, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_NOZORDER);
PostMessage(hwnd, WM_APP + 2, 0, 0);
ReleaseDC(hwnd, hdc);
fullScreen = true;
} else {
SetDisplayMode(0, 0, 0, 0);
#if ENABLE_RESIZE
SetWindowLongPtr(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
#else
SetWindowLongPtr(hwnd, GWL_STYLE, (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX /*| WS_MAXIMIZEBOX*/));
#endif
SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
int iWindowWidth = windowRect.right - windowRect.left;
int iWindowHeight = windowRect.bottom - windowRect.top;
SetWindowPos(hwnd, 0, windowRect.left, windowRect.top, iWindowWidth, iWindowHeight, SWP_NOZORDER);
PostMessage(hwnd, WM_APP + 1, 0, 0);
fullScreen = false;
}
OpenGLResetProjection();
}
void SetDisplayMode(int width, int height, int bpp, int refreshRate) {
if(width == 0 && height == 0 && bpp == 0 && refreshRate == 0) {
ChangeDisplaySettings(NULL, 0);
return;
}
DEVMODE dm;
dm.dmSize = sizeof(DEVMODE);
int i = 0;
while(EnumDisplaySettings(NULL, i++, &dm)) {
if(dm.dmPelsWidth == width && dm.dmPelsHeight == height &&
dm.dmBitsPerPel == bpp && dm.dmDisplayFrequency == refreshRate) {
if(ChangeDisplaySettings(&dm, CDS_TEST) == DISP_CHANGE_SUCCESSFUL) {
ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
return;
}
}
}
}
void OpenGLResetProjection() {
RECT window = { 0 };
GetClientRect(hwnd, &window);
glViewport(0, 0, window.right - window.left, window.bottom - window.top);
float fov = 62.0f;
float aspect = (float)(window.right - window.left) / (float)(window.bottom - window.top);
float top = OGL_NEAR * float(tanf(fov * 3.14159265f / 360.0f));
float bottom = -1.0f * top;
float right = bottom * aspect;
float left = top * aspect;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(left, right, bottom, top, OGL_NEAR, OGL_FAR);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
Resize(int(window.right - window.left), int(window.bottom - window.top));
}
#if MULTITHREADED
unsigned int __stdcall GameThread(void *args) {
const unsigned int signalstart = 0;
const unsigned int signalstop = 1;
HANDLE* hgamesignals = (HANDLE*)args;
bool returnIsDown = false, returnWasDown = false;
bool f4Processed = false;
#if THROTTLE_FPS
const int SKIP_TICKS = 1000 / TARGET_FPS;
DWORD next_game_tick = GetTickCount();
int sleep_time = 0;
#endif
while (WaitForSingleObject(hgamesignals[signalstart], 0) != WAIT_OBJECT_0)
Sleep(10);
HDC hdc = GetDC(hwnd);
wglMakeCurrent(hdc, hglrc);
ToggleFullscreen(true, false);
OpenGLResetProjection();
Initialize();
do {
#if THROTTLE_FPS
next_game_tick += SKIP_TICKS;
sleep_time = next_game_tick - GetTickCount();
if(sleep_time >= 0)
Sleep(sleep_time);
#endif
returnIsDown = bool(GetAsyncKeyState(VK_RETURN) != 0);
Update();
Render();
SwapBuffers(hdc);
if (WaitForSingleObject(hgamesignals[signalstop], 0) == WAIT_OBJECT_0)
break;
if ((GetAsyncKeyState(VK_LMENU) || GetAsyncKeyState(VK_RMENU)) && (returnIsDown && !returnWasDown))
ToggleFullscreen();
if ((GetAsyncKeyState(VK_LMENU) || GetAsyncKeyState(VK_RMENU)) && (GetAsyncKeyState(VK_F4) && !f4Processed)) {
f4Processed = true;
PostMessage(hwnd, WM_DESTROY, 0, 0);
}
returnWasDown = returnIsDown;
} while (true);
Shutdown();
ToggleFullscreen(false, true);
wglMakeCurrent(NULL, NULL);
ReleaseDC(hwnd, hdc);
ReleaseMutex(hgamesignals[signalstart]);
ReleaseMutex(hgamesignals[signalstop]);
return 0;
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment