-
-
Save seece/1f67da14d69b9c8a75a7e6839abf8e72 to your computer and use it in GitHub Desktop.
WinAPI window creation and key input handling example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "logger.h" | |
#include "deka.h" | |
#include "system.h" | |
#include "util.h" | |
#include <cstdlib> | |
#ifdef DEBUG | |
#include "GLDebug.h" | |
#endif | |
#include "gl/eksgl.h" | |
bool system_running; | |
int window_width = 0; | |
int window_height = 0; | |
struct Window { | |
HDC deviceContext; | |
HWND handle; | |
HGLRC renderingContext; | |
WNDCLASSEX windowClass; | |
bool fullscreen; | |
}; | |
#define DEKA_KEY_AMOUNT (256) | |
namespace { | |
bool keyDown[DEKA_KEY_AMOUNT]; | |
bool keyHit[DEKA_KEY_AMOUNT]; | |
bool mouseLeft = false; | |
bool mouseRight = false; | |
int frames_rendered; | |
int mousex; | |
int mousey; | |
int mousedeltaz; | |
const char* gl_extension_string = ""; | |
LARGE_INTEGER programStartTime; | |
LARGE_INTEGER counterFrequency; | |
} | |
bool system_gl_ext_supported(const char* name) | |
{ | |
return strstr((const char *)gl_extension_string, name) != NULL; | |
} | |
LRESULT CALLBACK WndProc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam) { | |
if (message == WM_DESTROY) { | |
PostQuitMessage(0); | |
return 0; | |
} else | |
return DefWindowProc(windowHandle, message, wParam, lParam); | |
} | |
// Lifted from GIGAENGINE code. | |
static Window create_window(int width, int height, bool fullscreen, bool vsync) | |
{ | |
window_width = width; | |
window_height = height; | |
Window win = { 0 }; | |
auto& windowClass = win.windowClass; | |
win.fullscreen = fullscreen; | |
windowClass.cbSize = sizeof(windowClass); | |
windowClass.hInstance = GetModuleHandle(0); | |
windowClass.style = CS_OWNDC; | |
windowClass.hIcon = LoadIcon(windowClass.hInstance, MAKEINTRESOURCE(129)); | |
windowClass.lpfnWndProc = WndProc; | |
windowClass.lpszClassName = "klassy klass"; | |
ZeroMemory(keyDown, DEKA_KEY_AMOUNT * sizeof(bool)); | |
RegisterClassEx(&windowClass); | |
HWND temporaryWindow = CreateWindowEx(WS_EX_APPWINDOW, "klassy klass", "temp", WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX, 0, 0, 0, 0, 0, 0, windowClass.hInstance, 0); | |
HDC temporaryDeviceContext = GetDC(temporaryWindow); | |
PIXELFORMATDESCRIPTOR pixelFormat = { 0 }; | |
pixelFormat.nSize = sizeof(pixelFormat); | |
pixelFormat.nVersion = 1; | |
pixelFormat.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; | |
pixelFormat.cColorBits = 32; | |
pixelFormat.cDepthBits = 24; | |
SetPixelFormat(temporaryDeviceContext, ChoosePixelFormat(temporaryDeviceContext, &pixelFormat), &pixelFormat); | |
HGLRC temporaryRenderingContext = wglCreateContext(temporaryDeviceContext); | |
wglMakeCurrent(temporaryDeviceContext, temporaryRenderingContext); | |
const char* extensions = (const char*)glGetString(GL_EXTENSIONS); | |
int ext_string_length = strlen(extensions) + 1; | |
gl_extension_string = new const char[ext_string_length]; | |
memcpy((void*)gl_extension_string, extensions, ext_string_length); | |
const int formatAttributes[] = | |
{ | |
WGL_DRAW_TO_WINDOW_ARB, 1, | |
WGL_SUPPORT_OPENGL_ARB, 1, | |
WGL_ACCELERATION_ARB, 0x2027, | |
WGL_DOUBLE_BUFFER_ARB, 1, | |
WGL_PIXEL_TYPE_ARB, 0x202B, | |
WGL_COLOR_BITS_ARB, 32, | |
WGL_DEPTH_BITS_ARB, 24, | |
0 | |
}; | |
const int contextAttributes[] = | |
{ | |
WGL_CONTEXT_MAJOR_VERSION_ARB, 4, | |
WGL_CONTEXT_MINOR_VERSION_ARB, 2, | |
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, | |
//WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, | |
#ifdef DEBUG | |
WGL_CONTEXT_FLAGS_ARB, 0 | WGL_CONTEXT_DEBUG_BIT_ARB, | |
#endif | |
0 | |
}; | |
int format, formatcount; | |
RECT windowArea = { 0, 0, width, height }; | |
DWORD displayFlags = WS_POPUP; | |
if (fullscreen) { | |
DEVMODE dev = { 0 }; | |
dev.dmSize = sizeof(DEVMODE); | |
dev.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL; | |
dev.dmPelsWidth = width; | |
dev.dmPelsHeight = height; | |
dev.dmBitsPerPel = 32; | |
ChangeDisplaySettings(&dev, CDS_FULLSCREEN); | |
} else { | |
displayFlags = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_BORDER; | |
//displayFlags = WS_POPUP ; // useful for frame dumping | |
AdjustWindowRect(&windowArea, displayFlags, 0); | |
windowArea.right -= windowArea.left; | |
windowArea.bottom -= windowArea.top; | |
windowArea.left = (GetSystemMetrics(SM_CXSCREEN) - windowArea.right) / 2; | |
windowArea.top = (GetSystemMetrics(SM_CYSCREEN) - windowArea.bottom) / 2; | |
} | |
win.handle = CreateWindowEx(WS_EX_APPWINDOW, | |
"klassy klass", | |
DEKA_DEMO_NAME, | |
displayFlags, | |
windowArea.left, | |
windowArea.top, | |
windowArea.right, | |
windowArea.bottom, | |
0, | |
0, | |
windowClass.hInstance, | |
0); | |
win.deviceContext = GetDC(win.handle); | |
((PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"))(win.deviceContext, formatAttributes, 0, 1, &format, (UINT*)&formatcount); | |
SetPixelFormat(win.deviceContext, format, &pixelFormat); | |
win.renderingContext = ((PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB"))(win.deviceContext, 0, contextAttributes); | |
if (win.renderingContext == NULL) { | |
die("Couldn't create context."); | |
} | |
wglMakeCurrent(win.deviceContext, win.renderingContext); | |
wglDeleteContext(temporaryRenderingContext); | |
ReleaseDC(temporaryWindow, temporaryDeviceContext); | |
DestroyWindow(temporaryWindow); | |
MSG message; | |
while (PeekMessage(&message, 0, 0, 0, PM_REMOVE)); | |
/* | |
if (ogl_LoadFunctions() == ogl_LOAD_FAILED) { | |
die("Couldn't init OpenGL functions!"); | |
} | |
*/ | |
int failed = load_gl_functions(); | |
if (failed > 0) { | |
trace("Failed to load %d GL functions.", failed); | |
} | |
trace("Vendor : %s\nRenderer : %s\nOpenGL version : %s\nGLSL version : %s\n", | |
glGetString(GL_VENDOR), | |
glGetString(GL_RENDERER), | |
glGetString(GL_VERSION), | |
glGetString(GL_SHADING_LANGUAGE_VERSION)); | |
/* | |
int major = -1; | |
int minor = -1; | |
glGetIntegerv(GL_MAJOR_VERSION, &major); | |
glGetIntegerv(GL_MINOR_VERSION, &minor); | |
trace("GL %d.%d\n", major, minor); | |
*/ | |
ShowWindow(win.handle, SW_SHOW); | |
glViewport(0, 0, width, height); | |
glClearColor(.0f, .0f, .0f, .0f); | |
glClear(GL_COLOR_BUFFER_BIT); | |
SwapBuffers(win.deviceContext); | |
return win; | |
} | |
static Window current_window; | |
bool system_init(int width, int height, bool fullscreen, bool vsync) | |
{ | |
#ifdef DEBUG | |
const char* datadir = "data"; | |
SetCurrentDirectory(datadir); | |
trace("Changed working dir to %s", datadir); | |
#endif | |
QueryPerformanceFrequency(&counterFrequency); | |
QueryPerformanceCounter(&programStartTime); | |
srand(static_cast<unsigned int>(programStartTime.QuadPart)); | |
//current_window = create_window(1280, 720, false, false); | |
current_window = create_window(width, height, fullscreen, vsync); | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
SwapBuffers(current_window.deviceContext); | |
#ifdef DEBUG | |
setupDebugOutput(); | |
#endif | |
frames_rendered = 0; | |
system_running = true; | |
return true; | |
} | |
double system_get_time() | |
{ | |
LARGE_INTEGER now; | |
QueryPerformanceCounter(&now); | |
return (now.QuadPart - programStartTime.QuadPart) / static_cast<double>(counterFrequency.QuadPart); | |
} | |
unsigned long long system_get_millis() | |
{ | |
LARGE_INTEGER now; | |
QueryPerformanceCounter(&now); | |
return (now.QuadPart - programStartTime.QuadPart) * 1000 / counterFrequency.QuadPart; | |
} | |
bool system_cleanup() | |
{ | |
trace("system_cleanup"); | |
if (current_window.fullscreen) | |
ChangeDisplaySettings(0, 0); | |
wglMakeCurrent(0, 0); | |
wglDeleteContext(current_window.renderingContext); | |
ReleaseDC(current_window.handle, current_window.deviceContext); | |
DestroyWindow(current_window.handle); | |
UnregisterClass("klassy klass", GetModuleHandle(0)); | |
return true; | |
} | |
// Max number of keypresses per frame is 128. | |
#define CHAR_BUFFER_SIZE (128) | |
static unsigned char charbuffer[CHAR_BUFFER_SIZE]; | |
static int charbuffer_pos; | |
static void add_input_character(unsigned char c) { | |
if (charbuffer_pos < CHAR_BUFFER_SIZE) { | |
charbuffer[charbuffer_pos++] = c; | |
} | |
} | |
int system_poll_input_char(unsigned short* out_c) { | |
if (charbuffer_pos > 0) { | |
*out_c = charbuffer[charbuffer_pos--]; | |
} | |
return charbuffer_pos; | |
} | |
void system_update() | |
{ | |
frames_rendered++; | |
MSG message; | |
ZeroMemory(keyHit, DEKA_KEY_AMOUNT * sizeof(bool)); | |
mousedeltaz = 0; | |
charbuffer_pos = 0; | |
while (PeekMessage(&message, 0, 0, 0, PM_REMOVE)) { | |
TranslateMessage(&message); | |
DispatchMessage(&message); | |
WPARAM wParam = message.wParam; | |
switch (message.message) { | |
case WM_QUIT: | |
system_running = false; | |
break; | |
case WM_KEYDOWN: | |
keyHit[wParam] = keyDown[wParam] = true; | |
break; | |
case WM_KEYUP: | |
keyDown[wParam] = false; | |
break; | |
case WM_LBUTTONDOWN: | |
mouseLeft = true; | |
break; | |
case WM_LBUTTONUP: | |
mouseLeft = false; | |
break; | |
case WM_RBUTTONDOWN: | |
mouseRight = true; | |
break; | |
case WM_RBUTTONUP: | |
mouseRight = false; | |
break; | |
case WM_MOUSEMOVE: | |
mousex = int(message.lParam) % 65536; | |
mousey = int(message.lParam) / 65536; | |
break; | |
case WM_MOUSEWHEEL: | |
mousedeltaz += GET_WHEEL_DELTA_WPARAM(wParam); | |
break; | |
case WM_CHAR: | |
// You can also use ToAscii()+GetKeyboardState() to retrieve characters. | |
if (wParam > 0 && wParam < 0x10000) { | |
//io.AddInputCharacter((unsigned short)wParam); | |
// FIXME this might be broken? | |
add_input_character((unsigned short)wParam); | |
} | |
break; | |
case WM_PAINT: | |
/*HDC hdc; | |
PAINTSTRUCT ps; | |
hdc = BeginPaint(current_window.handle, &ps); | |
//wglMakeCurrent(hdc, current_window.renderingContext); | |
//scene.Render(); // | |
//SwapBuffers(hdc); | |
//wglMakeCurrent(hdc, 0); | |
EndPaint(current_window.handle, &ps);*/ | |
break; | |
} | |
} | |
} | |
void system_draw() | |
{ | |
SwapBuffers(current_window.deviceContext); | |
} | |
bool system_key_down(int code) | |
{ | |
passert(code >= 0); | |
passert(code < DEKA_KEY_AMOUNT); | |
return keyDown[code]; | |
} | |
bool system_key_hit(int code) | |
{ | |
passert(code >= 0); | |
passert(code < DEKA_KEY_AMOUNT); | |
return keyHit[code]; | |
} | |
MousePos system_mouse_pos() | |
{ | |
return { mousex, mousey, mousedeltaz }; | |
} | |
bool system_mouse_down(int button) | |
{ | |
if (button == 0) | |
{ | |
return mouseLeft; | |
} | |
if (button == 1) | |
{ | |
return mouseRight; | |
} | |
#ifdef DEBUG | |
die("invalid mouse button %d", button); | |
#endif | |
return false; | |
} | |
void* system_get_window_handle() | |
{ | |
return (void*)current_window.handle; | |
} | |
HDC system_get_device_context() | |
{ | |
return current_window.deviceContext; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#define WIN32_LEAN_AND_MEAN | |
#define WIN32_EXTRA_LEAN | |
#include <Windows.h> // for VK key codes | |
extern int window_width; | |
extern int window_height; | |
struct MousePos { | |
int x, y; | |
int dz; // wheel movement | |
}; | |
// main loop runs as long as this is true | |
extern bool system_running; | |
bool system_init(int width, int height, bool fullscreen, bool vsync); | |
bool system_cleanup(); | |
void system_update(); | |
void system_draw(); | |
// These use WinAPI virtual key codes, e.g. VK_ESCAPE | |
bool system_key_down(int code); | |
bool system_key_hit(int code); | |
MousePos system_mouse_pos(); // returns the mouse position in pixels | |
bool system_mouse_down(int button); // 0 = left, 1 = right | |
int system_poll_input_char(unsigned short* out_c); // returns the number of input characters left in buffer | |
bool system_gl_ext_supported(const char* name); | |
double system_get_time(); | |
unsigned long long system_get_millis(); | |
void* system_get_window_handle(); // cast to HWND manually | |
HDC system_get_device_context(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment